(Django) 프로젝트와 앱만들기의 기본

Kepler·2020년 2월 2일
2

Django

목록 보기
5/12

Step-by-step Django Tutorial

1. 프로젝트 만들기: $ django-admin startproject 웹사이트 이름

  1. 위의 코드는 project 디렉토리를 만들고, root-directory 바로 밑에 manage.py를 포함한 여러가지 파일들을 만들어 준다.
  2. 또한, project이름으로 된 또다른 디렉토리를 만들며, 그 안에도 여러가지 바로 초기 파일을 만들어 준다. (주의 : root-directory의 이름은 바꿔도 되나, 자동생성된 디렉토리의 이름은 바꿔서는 안된다.)
  3. 2의 디렉토리에는 아래 4가지 파일이 자동으로 생성된다.
    • __init__.py: 파이썬 패키지
    • wsgi.py: special type of web server
    • settings.py: overall settings for the entire website
    • urls.py: table of contents for the website : look at the url and perform the functionality

2. URL RESOLUTION (urls.py)

최상위 URL RESOLUTION은 다음과 같이 작성할 수 있다.

기본으로 제공되는 urls.py의 내용이 복잡해질 경우, 각각의 app의 디렉토리 밑에 urls.py를 새로 만들어 주고, 최상위 urls.py 에서 각각에 접근할 수 있도록, urlpatterns 리스트에 추가해 준다.

주의: app의 url을 추가할 경우, 그 작업을 다 완료한 후에 project의 url은 알맞게 바꾸어 준다.

@ urls.py

from django.contrib import admin # admin 사이트 URL생성용 모듈
from django.urls import include, path # 유저에게 보여줄 사이트 URL생성용 모듈

urlpatterns = [
    path('polls/', include('polls.urls')), #include는 다른 urls.py를 참조하러 갈 수 있게 해준다
    path('admin/', admin.site.urls),
]

app 디렉토리 하위에 urls.py 를 생성할경우, 다음의 코드를 shell 에서 입력해준다.
( url파일을 복사하여 생성하는 방법이다.)

$ cp <project이름>/urls.py <app이름>/.

각각의 app에 만든 하위 urls.py는 path에서 VIEW의 정보를 argument 로 받기 때문에, 반드시 views.py를 import해야한다.

장고에는 많은 framework에 내장된 모듈이 많이 존재한다.
그래서 필요에따라 다양한 요소들을 import해서 사용할 수 있다.

from django.urls import path    #path 모듈을 django.urls부터 import
from . import views

urlpatterns = [
    path('', views.index, name='index'), 
  # arg1: 도메인형식
  # arg2: render해줄 페이지
  # arg3: name (template 작성시 유용하게 쓰임, 필수아님?)
]

2. app만들기 : $ python manage.py startapp 앱 이름

위의 코드를 실행하면 다음의 파일들이 default로 생성된다.

  • migrations 디렉토리: hook-up your website (sourcode with database)
  • __init__.py
  • admin.py : every website has admin section: Django creates for you by default.
  • apps.py: settings.py for app
  • models.py: blueprint of how you are going to store data for the app (where you create Classes).
  • views.py : python fuction that takes a user's request and give back http responses. (mostly web pages that they requested, such as html)

3. Database 셋업 (settings.py):

  • INSTALLED APPS : settings.py에 있는 리스트 객체로써, 새로 만든 app은 이곳에 address를 입력하여, 데이터베이스와 싱크할 수 있도록 셋팅해주어야 한다.
    추가된 app은 가장 밑에 순차적으로 적어준다.
    첫번째 경로에는 app이름, 두번째 경로는 apps이다 (apps.py에 Config가 들어있기 때문에)
    마지막 경로의 이름은 디폴트로 카멜형식의 app이름+Config으로 만들어 진다.
@ settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls.apps.PollsConfig',      
]
  • 추가 후, shell에서 아래의 코드를 실행하여 migrate을 해준다.
    (이외에도 models.py에 attributes를 추가할 경우에도 migration이 필요하다.)
$ python manage.py migrate 
# look for INSTALLED_APPS in settings.py and see what tables are needed. 
  • 이외 settings.py에는 다음의 항목들이 존재한다.

    • BASE_DIR : manage.py 파일이 존재하는 디렉토리

    • SECRET_KEY : 암호화된 서명을 필요로하는 장고 앱에 사용할 수 있는 키. 임의의 설정 가능.

    • DEBUG : TEST시 변수들의 다양한 상태 확인 가능, 배포시 FALSE

    • INSTALLED_APPS : pip설치, 또는 본인이 만든 app을 추가 한다.

    • MIDDLEWARE_CLASSES : 인증, 보안관련

    • TEMPLATES : template관련된 설정을 할수 있다. (3세대 부터는 거의 사용X)

    • DATABASES : 데이터베이스 엔진에 연결 설정 (접속기 역할)

    • static_url : 정적페이지를 만들 때 사용한다 (css,images,etc), 역시 3세대 부터는 거의 쓰이지 않는다.

4. MODEL 만들기 (models.py) :

  • MODEL은 데이터베이스와 통신을 하여, 데이터를 가지고 오는 역할을 담당하며 models.py는 이와 관련된 파일이다. 데이터를 서버에 저장하기 위해서는 데이터베이스가 필요하다. 장고는 장고는 ORM을 제공하는데, 이것을 통하여 models.py를 통해 쉽게 DB를 관리할 수 있다.

ORM(Object Relational Mapping), 객체-관계 매핑
객체와 관계형 데이터베이스의 데이터를 자동으로 mapping(연결)해주는 것을 말한다. 객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용한다.
객체 모델과 관계형 모델 간에 불일치가 존재한다.ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
DB data <—mapping—> Object field 객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.

  • 이곳에서 정의된 각각의 class는 데이터베이스의 table에 데이터를 적절히 넣어주는 역할을 한다.

A model is the single, definitive source of truth about your data.
Django follows the DRY Principle. The goal is to define your data model in one place and automatically derive things from it.

Don’t repeat yourself (DRY)
Every distinct concept and/or piece of data should live in one, and only one, place. Redundancy is bad. Normalization is good.

  • 데이터베이스의 각 필드는 class의 instance로써 표현된다.
    + 예제: CharField 는 문자필드, DateTimeField는 날짜와 시간(datetime) 필드
    • 각 필드가 가질수 있는 자료형을 Django에게 전달한다.
  • class의 각각의 attribute는 데이터베이스의 column명이 된다.
    (예제: question_text & pub_date)
  • django.db 모듈에서 models를 import해야, 연결이 된다.
  • def __str__(self) : 해당 객체의 문자열 represntation. 대화식 프롬프트에서 편하게 보려는 이유 말고도, Django 가 자동으로 생성하는 관리 사이트 에서도 객체의 표현이 사용되기 때문에 설정해준다.
  • models.py의 내용을 변경할 경우, 확인하려면 shell을 재시작해야한다.
@ models.py

from django.db import models  # ORM이 가능하도록 장고에서 models를 import

class Question(models.Model):   # models의 내장함수인 Model을 parameter로 받음.
    question_text = models.CharField(max_length=200) #max_length는 CharField의 필수 입력값
    pub_date = models.DateTimeField('date published') #argument는 참고하기 위해 human friendly한 문자열로 pub_date의 역할을 설명. argument가 비어도 상관없다.
    
    def __str__(self):
       return self.question_text #위에서 정의된 question_text 변수를 문자열로 retrun
    
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) #ForeignKey(id#)를 사용하여, Question 과 Choice 클래스를 연결시킨다. #on_delete:models.CASCADE: Question을 지우면 Choice도 같이 지워진다
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

5. MODEL 수정내용 반영하기 : makemigrations & migration

  • models.py를 수정했으므로, 이를 데이터베이스에 반영하기 위하여 migration 작업이 필요하다.
  • settings.py에서 INSTALLED_APPS 에 app의 address를 먼저 세팅한다. (2번스텝참조)
  • makemigrations : 모델의 수정내용을 점검하여, DB의 structure를 바꾸어 준다. 이 단계에서는 migrate이 가능한지를 점검해준다. (실제 migration이 일어나지는 않음)
  • migrate : 실제로 업데이트를 데이터베이스에 반영한다.
  • migrations 디렉토리의 initial.py에서 수동으로 변경도 가능하다.
$ python manage.py makemigrations 앱이름           #make change files
$ python manage.py migrate                        #actually runs it

6. 관리자 계정 만들기 (admin.py)

  • 관리자 계정을 생성한다.
$  python manage.py createsuperuser    
  • 새로 만든 app을 관리자 계정에서 관리하고 싶은 경우, 다음과 같이 설정이 필요하다.
@admin.py

from .models import 앱이름

admin.site.register(앱이름)

7. 클라이언트의 요청 처리하기 (views.py):

  • views.py는 데이터를 MODEL에서 가져와 가공하여, 웹페이지 결과를 만들도록 컨트롤 하는 역할을 한다.
  • views.py안에 있는 하나의 함수(또는 클래스)가 하나의 VIEW를 정의한다
  • VIEW는 HTTP Request를 입력 parameter로 받아들이고, HTTP Response를 리턴한다.
    http://pythonstudy.xyz/python/article/306-Django-%EB%B7%B0-View
  • Response는 크게 두가지 형태가 있다.

1. 요청된 페이지의 내용이 담긴 HttpResponse 객체를 반환, 아래의 여러가지 방법으로 실현 가능:

  • HttpResponse: 클라이언트의 request에 답하여 서버에서 클라이언트에게 보내는 정보. 예) 날씨웹서비스의 경우, 사용자가 요청한 위치의 날씨 정보를 제공한다.
    + 가장 기본적인 HttpResponse 객체의 return 방법.
    + HTTP코드가 200(ok)이고, constructor에 전달된 콘텐츠를 가지는 HttpResponse 객체를 새로 만든다.
    + 보통 작은 response에서만 쓴다.
from django.http import HttpResponse

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)
  • HttpResponseRedirect
    + HTTP코드가 302(Found/moved temporarily)인 새로운 HttpResponse 객체를 만든다.
    • 다른 페이지로 redirect 할 경우에만 쓰여야 한다 (예: POST전송 성공시)
    • response의 수가 증가할 경우, HttpResponse 보다 더 효과적.
    • HttpResponseRedirect는 유저에게 재전송될 URL이라는 단 하나의 argument를 받음.
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse

from .models import Choice, Question
# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
# reverse: VIEW의 함수로부터 역으로 URL을 계산하므로, URL이 바꼈을 때, 변경이 누락되는 일이 없다. (하드코드가 아니기 때문에)
# reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
      
  • render *template도 같이 만들 때 사용, REST API에는 해당X
  • loader *template도 같이 만들 때 사용, REST API에는 해당X

2. 예외를 발생 시킴

  • try/except
from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})
  • get_object_or_404() shortcut
@ views.py

# Request/Response 처리를 위해 필요한 모듈들을 import
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

# MODEL의 클래스들을 import (중요!!)
from .models import Choice, Question

class IndexView(generic.ListView):
    template_name = 'polls/index.html'    #템플렛의 정의
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'
  • request 입력을 위한, URL정보의 분석은 app/urls.pyurlpatterns 리스트에 입력된 각각의 패턴을 참조하여 이루어진다.
@ app/urls.py

from django.urls import path
from . import views

app_name = 'polls'

urlpatterns = [
    # ex: /polls/
    path('', views.IndexView.as_view(), name='index'),
    # ex: /polls/5/
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    # ex: /polls/5/results/
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

참고: Rendering Templates with HttpResponse

  • HttpResponse와 동시에 templates을 생성해주는 방법
  1. loader를 활용한 template rendering:
@ views.py
from django.template import loader     #loader를 import
from .models import Choice, Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
#loader가 제공하는 get_template 메소드를 사용하여 template과 연결
    template = loader.get_template('polls/index.html')    
    context = {'latest_question_list': latest_question_list} 
#HttpResponse를 return
    return HttpResponse(template.render(context, request)) 
  1. render를 사용한 shortcut
  • template에 context를 채워넣어 표현한 결과를 HttpResponse 객체와 함께 돌려준다
  • django.shorcuts 모듈에서 render 함수를 import한다.
  • render 는 Httpresponse를 return에 이미 포함하고 있다
  • render 는 기본 3개의 arguments 를 받는다.
    arg1 = request (fixed)
    arg2 = 유저에게 보여줄 template address
    arg3 = template이 사용할 context

render()
render(request, template_name, context=None, content_type=None, status=None, using=None)

@views.py

from django.shortcuts import get_object_or_404, render  #import render 
...
from .models import Choice, Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context) 
  1. generic code 이용: https://velog.io/@rosewwross/Django-Using-Generic-Views

참고: TEMPLATES 만들기

  • TEMPLATE에는 VIEW 에서 받은 정보로 HTML파일등 UI를 가공하는 로직이 들어가 있다.
  • setting.py에서 TEMPLATE list에서 초기 상태를 확인 할 수 있다.
  • DjangoTemplates은 각각의 INSTALLED APPS에서 templates이라는 이름의 sub directory를 찾는다. 따라서, 앱을 새로 만들때 그 안에 templates이라는 이름의 디렉토리를 만들어야 한다.
  • 생성된 sub-directory 안에 또다시 앱이름의 디렉토리를 만드는데, 이는 장고가 다른 앱에 같은 이름을 가진 templates을 불러오는것을 방지하게 위해서 이다.
  • html안에서 장고 코드를 입력하려면 아래의 규칙을 따른다
    • use a special syntax {% put your code here %} if you want to end {% endfor %}
    • for plain variables {{ put variables here }}
@ #index.html
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
      <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

10. Database에 object를 추가하기

방법1 : add one by one

  • go to admin.py and import the new class (if you want to manage it in admin)
  • No need for migration, unless you add/delete attribute.
    You don't need to migrate if you just adding methods using the existing attributes.
  • Actual update must be done in manage.py
  • 자주 사용하는 objects의 메소드들:
    클래스이름.objects.all() : shows all objects in a given class
    클래스이름.objects.filter(attribute__startswith="") : filter out objects starts with ""
    클래스이름.objects.get(pk=1): get 1 object with a primary key 1.
    .save() : save to DB
$ python manage.py shell

from 앱이름.models import 클래스1, 클래스2

new_object = Class1()
new_object.attribute1 = ..
new_object.attribute2 = ..

방법2 :create, save, put it in set all at once.

  • 클래스1 과 클래스2를 ForeignKey로 묶어줄 경우, 클래스1은 클래스2를 set로 가지게 된다.
  • 클래스1객체.클래스2_set.all() 클래스2 must be lowercase
  • 객체 생성 및 저장 : 클래스1객체.클래스2_set.create(attribute = "")
question1.choice_set.all() 
>>> <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Wecode>]>

# automatically saves to DB without using .save()
question1.choice_set.create(choice_text="coding")  
profile
🔰

2개의 댓글

comment-user-thumbnail
2021년 10월 5일

잘보고가요~

1개의 답글