본문 바로가기
Research/Django

[Django] 공식 튜토리얼 2 정리

by RIEM 2021. 11. 3.

Django 공식 튜토리얼 2 정리

작성일 : 2021-11-02

레퍼런스

 

데이터베이스 세팅

> mysite/settings.py

DB를 세팅해보자. mysite/settings.py은 장소 세팅을 담당하는 모듈레벨 변수들과 함께인 일반 파이썬 모듈이다. SQLite가 기본으로 설정되어있다. SQLite는 쉽고 간편하다. 파이썬으로 활용할 수 있으며 따로 설치할 필요가 없다. 하지만 프로젝트를 제대로 진행할 때는, PostgreSQL와 같은 확장가능한 DB를 사용하는 것이 좋다.


DATABASES

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
        }
}

 

다른 DB를 사용하고자 한다면, 적덜한 DB 를 설치하고 Database의 default 세팅 관련 keys들을 바꿔주어야 한다.

 

Engine 예시

  • ‘django.db.backends.sqlite3’
  • ‘django.db.backends.postgresql’
  • ‘django.db.backends.mysql’

Name : DB의 이름을 기재한다. SQLite를 사용할 경우 해당 데이터베이스는 저장되어 있을 것이라 한다. NAME은 파일 이름을 포함한 full 절대 경로여야 한다. 기본 세팅인 ‘BASE_DIR / ‘db.sqlite3’이 프로젝트 디렉토리를 포함하고 있다.

 

만약 SQLite를 DB로 사용하지 않을 경우, USER, PASSWORD, HOST를 추가로 입력해야 한다. 


TIME_ZONE

TIME_ZONE도 우리가 위치한 곳의 시간 기준으로 수정해주면 좋다.


INSTALLED_APPS

여기는 장고 앱들의 이름이 나열되어있다. 앱들은 장고 인스턴스에서 활성화되어있다. 앱들은 다양한 프로젝트들에서 사용될 수 있는데, 이들을 package하고 distribute해서 다른 프로젝트에도 사용할 수 있다.

기본적으로 INSTALLED_APPS는 아래와 같은 앱들을 디폴트로 포함하고 있다.

 

INSTALLED_APPS = [

    'Django.contrib.admin', # 관리자 사이트
    'Django.contrib.auth', # 인증 시스템
    'Django.contrib.contenttypes', # 콘텐트 유형을 위한 프레임워크
    'Django.contrib.sessions', # 세션 프레임워크
    'Django.contrib.messages', # 메시지 프레임워크
    'Django.contrib.staticfiles', # 이미지 등 static 파일들을 관리하는 프레임워크

]

 

이 앱들의 일부는 적어도 하나의 DB 테이블을 이용한다. 따라서 그것들을 사용하기 전에 database에 테이블들을 만들 필요가 있다.


> DB에 테이블 생성하기

input

$ python manage.py migrate

Output

무언가 많은 것이 떴고 정상적으로 시행된 것 같다. Migrate 명령어가 INSTALLED_APPS 세팅을 확인한다. 그다음 우리의 mysite/settings.py파일과 DB migrations에 있는 데이터베이스 환경설정에 따라 필요한 데이터베이스 테이블들을 만들어낸다. 각 migration에 적용이 되었다는 메시지를 위에서 확인할 수 있다. 이는 각 DATABASE에서도 확인해볼 수 있다.

 

Models 생성하기

이제 Model들을 정의해보자. Model은 데이터베이스 레이아웃과 메타데이터를 가지고있는 필수적인 개념이다.

 

우리가 만들 Poll앱에서는 2개 모델을 만들 것인데, QuestionChoice다. Question은 지문과 게시날짜를 포함한다. Choice는 두개의 필드를 가지고 있는데, 텍스트 선택지와 투표 합계다. 각 ChoiceQuestion과 연동된다.

> polls/models.py를 열어 Model을 생성해보자.

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

 

자, 한번 보자. 각 모델은 서브클래스를 둔 django.db.models.Model 클래스로 표현된다. 각 모델은 복수의 클래스 변수들을 가지고 있는데, 각각 모델의 데이터베이스 필드를 나타낸다.

 

각 필드는 어떤 유형의 데이터를 가지고있는지를 장고에게 알려주며, 이들은 Field 클래스의 인스턴스로 표현된다.. 예를 들어 CharField는 문자열 필드를 표현하고 DAteTimeField는 날짜시간을 나타낸다. 그 외 다양한 유형의 필드가 있으니 공식문서를 참고해보면 좋을 것 같다.

 

question_text , pub_date와 같은 필드 인스턴스의 이름은 필드의 이름이며 기계어에 가깝다. 이것들은 파이썬 코드에서 value값으로 활용하고 DB에서 column 이름으로 활용한다. 우리는 optional first positional argument를 Field에 사용해서 사람이 읽기 편한 이름으로 활용할 수도 있다. 

 

어떤 필드들은 argument들을 요구하기도 한다. 예를들어 CharFieldmax_length를 넣어야 한다. 이는 DB 스키마에서 사용될 뿐만 아니라 인증에서도 사용된다고 한다. Field는 다양한 optional arguments들을 가지고 있는데, 이번 앱에서는 투표의 default 값을 0으로 설정하는 것으로 해보자. 

 

마지막으로, ForeignKey를 사용해서 관계를 정의해야한다. ForeignKey는 각 Choice가 단일 Question과 관련있다는 것을 장고에게 알려준다. 장고는 모든 공통 DB 관계를 지원하는데, many-to-one, many-to-many 그리고 one-to-one 방식들에 대한 지원이 가능하다고 한다.

 

Models 활성화하기

만든 Model들을 활성화시켜보자. 짧은 코드로 장고에게 많은 정보를 전달할 수 있다. 우리는 아래 두가지를 할것이다.

  • CREATE TABLE 선언 : 해당 앱의 DB 스키마 만들기
  • 파이썬 DB접근 API 생성 : QuestionChoice 객체들에 대한 접근을 위함


> 우선 프로젝트에게 polls 앱이 설치되어있다고 전달해줘야 한다.

앱을 현재 프로젝트에 포함시키기 위해, INSTALLED_APPS에 configuration class에 대한 참조를 추가해야 한다. PollsConfig 클래스는 polls/apps.py 파일 안에 있고, dotted path는 ‘polls.apps.PollsConfig’이다. mysite/settings.py 파일을 열고 해당 경로를 INSTALLED_APPS 환경에 추가해주자.

 

> mysite/settings.py

INSTALLED_APPS = [

    'polls.apps.PollsConfig', #polls - apps - PollsConfig 클래스
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

]

 

자, 이제 polls 앱이 장고에 추가되었다. 이를 장고에 반영해주자.


> makemigrations

Input

$ python manage.py makemigrations polls

Output

Polls에 대한 Migrations이 진행되었다는 문구가 떴다.

 

Migration에 대한 개념이 직관적으로 와닿지 않아 관련 정보를 찾던 중 아래 설명을 발견했다.

 

장고 공식 문서에서는 마이그레이션이 모델의 변경 내역을 DB *스키마에 적용시키는 장고의 방법이라고 설명하고 있습니다. 장고는 ORM(Object-Relational Mapping)을 사용하기 때문에 models.py와 클래스를 통해 DB 스키마를 생성하고 컨트롤 하게 되는데, 이 때 DB 스키마를 git처럼 버전으로 나눠서 관리 할 수 있게 해 주는 시스템이라 생각하시면 됩니다. 즉 하나의 마이그레이션 파일은 해당 마이그레이션이 생성된 시점의 모델의 구조(DB의 스키마)를 담고 있습니다.

(출처 : 개발하는중생 님의 블로그 https://tibetsandfox.tistory.com/24)

 

한 마디로 Migration은 이 파일이 생성된 시점의 모델 구조(DB 스키마)를 담고있는 것이며 이로 인해 DB스키마의 버전들을 관리할 수 있게 된다는 이점이 있다고 볼 수 있다.

 

Makemigrations를 실행한다는 것은 ‘장고야, 내가 model들을 수정했고 migration 반영해줘’라고 명령어를 내리는 것이다. 

Migration들은 장고가 model들에 변화를 저장하는 방식을 의미한다. 이들은 디스크에 있는 파일들이다. 원한다면 새로운 model에서 migration을 읽어낼 수 있다. 

 

아래 명령어는 migration을 실행하고 DB 스키마를 자동으로 관리해주는 migrate 명령어다. 

> Sqlmigrate를 해보자.

input

$ python manage.py sqlmigrate polls 0001


Output

결과메시지를 깔끔하게 표현하면 아래와 같다.

BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

 

  • 사용하는 DB에 따라서 결과 메시지가 다르게 나올 수도 있다.
  • 테이블 이름들은 앱 이름(polls)과 모델명(questions, choice)의 조합으로 자동생성된다.
  • Primary keys(IDs)는 자동으로 추가된다.
  • Foreign key 필드명에 “_id”가 추가된다.

> DB에 모델 테이블들을 생성하기 위해 migrate를 다시 진행해보자.

input

$ python manage.py migrate

output

 

Migrate 명령어를 입력하면 적용되지 않은 migrations들이 일괄적으로 적용된다. 그리고 이 과정을 통해 모델 변경사항들과 DB의 스키마의 동기화가 진행된다.

 

Migrations은 매우 강력한데 시간에 걸쳐 프로젝트를 진행함에 따라 모델들을 바꾸게 해주는데, 이때 DB 또는 테이블들을 지우고 새로 추가할 필요가 없다. 일종의 git과 같은 버전 컨트롤 시스템이라 보면 된다.

 

모델을 바꾸기 위한 3단계!

  • Models.py에서 모델을 바꾸기
  • Python manage.py makemigrations 로 변경된 migrations을 생성하기
  • Python manage.py migrate 로 변경사항을 DB에 적용시키기

 

API 활용하기

장고 API를 이용해보자.

Input

$ python manage.py shell

Output

 

‘Python’이라 선언하지 않고 활용할 수 있는데, manage.py가 DJANGO_SETTINGS_MODULE의 환경 변수를 두고있어서, 장고가 파이썬으로 하여금 당신의 mysite/settings.py파일의 경로를 임포트하도록 하기 때문이다. 

>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

위 <Question: Question object (1)> 부분은 이해하기가 어렵다. polls/models.py에 있는 Question 모델을 수정하고 __str__() 함수를 Question과 Choice에 각각 추가해서 고쳐보자.

 

> polls/models.py에 __str__() 함수 추가

from django.db import models

class Question(models.Model):

    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published') 

    def __str__(self):
        return self.question_text

class Choice(models.Model):

    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

 

각 클래스에 __str__(self) 함수를 추가해주었다. 이것을 모델에 추가하는 것은 중요한데 대화식 프롬프트에서 편하게 보는 것 뿐만아니라 장고가 자동으로 생성하는 어드민 페이지에서도 객체의 표현이 사용되기 때문이다.


 > 모델에 커스텀 메소드를 추가하기

was_published_recently() 메소드를 추가해주었다.

import datetime
from django.db import models
from django.utils import timezone

class Question(models.Model):

    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):

    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

 

Import datetime은 Python의 표준모듈이고, from django.utils import timezone은 장고의 시간대 유틸리티 참조를 위한 것이다. 


위 내용을 저장하고 다시 python manage.py shell로 돌아와서 아래를 실행해보자.

>>> from polls.models import Choice, Question

 

# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

관리자 생성하기

어드민 사이트에서 관리를 하게해주는 관리자 계정을 생성해보자

Input

$ python manage.py createsuperuser

Output

Username: admin

Email address: admin@example.com

Password: **********
Password (again): *********
Superuser created successfully.

어드민 계정이 만들어졌다. 

 

Input

$ python manage.py runserver

서버를 실행한 뒤에 http://127.0.0.1:8000/admin 주소로 들어가보자. 아래 어드민 사이트 로그인 화면이 보일것이다.

 

> 자신이 등록한 어드민 계정 아이디와 비밀번호를 넣고 로그인한다. 

 

로그인하면 이와 같은 관리자 페이지가 나온다. 이 페이지는 장고 인증 프레임워크인 django.contrib.auth 모듈에서 제공되는 페이지다.

 

관리자 페이지에서 poll앱 변경할 수 있도록 하기

Poll 앱은 관리자 인덱스 페이지에 보이지 않는다. 관리자에게 Question 객체들이 관리자 인터페이스를 가졌다고 알려줘야 한다. 이를 위해, polls/admin.py 파일을 열고 아래와 같이 수정해보자.

from django.contrib import admin
from .models import Question

admin.site.register(Question)

관리자 사이트를 들어가보니 POLLS에 Questions가 등록된 것을 확인했다. 

 

알아둘 것이 있다.

  • 이 형식은 Question 모델로부토 자동생성된 것이다.
  • 각 필드들의 유형들은 (DateTimeField, CharField) HTML input 위젯으로 표현된다. 각 필드들은 장고 관리자에서 어떻게 표현될지 알고 있다.
  • 각 DateTimeField 는 JavaScript 단축 기능과 연결된다. 날짜들은 “Today”단축 기능과 calendar popup 그리고 시간은 “Now” 단축 기능과 편리한 팝업 기능을 통해서도 얻는다.



댓글