Django - 2

21390 단어 장고장고

1. django tutorial 2


튜토리얼 1에 이어서 튜토리얼 2를 진행하자.

Part 2 - 1. 데이터베이스 설치


데이터베이스를 설치하자! 연동하자!
해당내용은 mysite/settings.py 에서 진행할 수 있다.

장고에서는 기본적으로 SQLite 라는 데이터베이스를 사용하도록 세팅되어 있다. 다른 데이터베이스(mysql이나 PostgreSQL 등)는 현재 튜토리얼에서는 진행하지 아니한다!

Settings.py 은 아래 항목들로 구성되어 있다.

  • INSTALLED_APPS
    프로젝트에서 사용할 앱들의 경로가 위치하는 영역, user defined app의 경로도 이 항목에 포함된다. 또한 함께 딸려오는 아래 앱들을 포함한다.

    • django.contrip.admin
      관리용 사이트
    • django.contrip.auth
      인증 시스템
    • django.contrip.contenttypes
      컨텐츠 타입을 위한 프레임워크
    • django.contrip.sessions
      세션 프레임워크
    • django.contrip.messages
      메세징 프레임워크
    • django.contrip.staticfiles
      정적 파일을 관리하는 프레임워크
  • TEMPLATES
    공통적으로 들어가는 html 코드를 관리하기 위한 확장형 template 들의 경로를 설정하는 영역

  • DATABASES
    database를 사용하기 위한 설정, default로 sqllite를 사용한다.

  • TIME_ZONE
    django의 DateTime 객체에서 사용할 기준 시간을 설정

  • STATIC_URL
    CSS, Image등의 정적 파일 경로를 설정

세팅파일을 에딧할 때, 시간대를 맞춰주자. TIME_ZONE 의 값을 바꿔주면 되는데,TIME_ZONE 의 값을 'Asia/Seoul'로 변경해주고 USE_TZ의 값을 False로 바꿔주자.

사담)
장고의 시간 처리 패키지는 크게 2개이다.
1. from datetime import datetime
2. from django.utils import timezone
모델에서 생성, 수정 시간을 기록하기 위해 보통 auto_now_add 와 auto_now를 사용한다. 이러면 해당 컬럼이 생성, 수정되었을 때의 시간이 자동으로 기록된다.

create_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now_True)

이 때, settings.py 에서 LANGUAGE_CODE 와 TIME_ZONE 의 설정을 각 각 'ko-kr' , 'Asia/Seoul' 이라고 설정을 하고 USE_TZ을 True로 유지한다면 DB는 UTC 시간이 저장될 것이다.

이어서)

위 INSTALLED_APPS에 딸려오는 어플리케이션들은 일반적인 경우에 사용하기 편리하도록 기본으로 제공되는 앱들이다.

이러한 기본 앱들 중 몇몇은 최소한 하나 이상의 데이터베이스 테이블(Database Table)을 사용하는데, 그러기 위해선 데이터베이스에서 테이블을 미리 만들 필요가 있다.

이를 위해서, 아래 명령을 실행하자.

$ python manage.py migrate

migrate 명령은 INSTALLED_APPS 의 설정을 탐색해서, settings.py의 데이터베이스 설정과 app과 함께 제공되는 데이터베이스 migrations에 따라서, 필요한 데이터베이스 테이블을 생성한다. 위 명령을 실행하면 각 migration 이 적용되는 메세지가 화면에 출력되는 것을 확인할 수 있다.

어떤 내용이 생성되었는지 궁금하다면, 데이터베이스 클라이언트로 접속한 후 아래 명령어를 통해 Django가 생성한 테이블을 확인해 볼 수 있다.

  1. PostgreSQL - \dt
  2. MariaDB, MySQL - SHOW TABLES;
  3. SQLite - tables
  4. Oracle - SELECT TABLE_NAME FROM USER_TABLES;

INSTALLED_APPS의 어플리케이션들은 기본적으로 제공되는 어플리케이션이므로, 일반적인 상황을 가정하여 제공하고 있다. 하지만 이러한 어플리케이션들이 모두에게 필요한 것들은 아니니, 일부는 사용할 필요가 없다고 생각한다면 해당 내용을 주석처리(삭제해도 무방하나 기록을 냅두는게 좋지 않을까?)한 후 migrate 명령을 진행해주자.

Part 2 - 2. 모델 만들기


모델 모듈(Model module 이하 모델) 이란 부가적인 메타데이터를 가진 데이터베이스의 구조(layout)를 말한다. 쉽게 설명하면 모델을 통해 데이터베이스와 연결하고, 데이터베이스에 영구적으로 데이터를 저장하거나 불러오게 해준다는 소리다. 이러한 작업 ORM(Object - Relation Mapping) 이라고 한다.

즉, models.py 에 Model class 작성을 통해서 database의 table과 매핑을 시켜주는 것이다.

polls app 에서 Question 과 Choice 라는 두 가지 모델을 만들 것이다.
한 번 만들어보자. polls 의 models.py 를 열어서 아래 코드를 넣어주자.

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)

코드를 이해해보자. 위에서 말한 것처럼 모델은 클래스로 작성된다. 우선 Qustion class 와 Choice class를 만들어 줬고, 매개변수로 models.Model을 넣어줬다. 즉, 위 from,,import 구문과 같이 생각해보면, django.db.models.Model 을 사용한다는 것이다.

각 모델에는 여러개의 변수(클래스 변수)가 있는데 이 변수들은 모델의 데이터베이스 필드로 표현된다.

참고) Database 의 Field란 Column을 의미한다. 즉, 타이틀을 의미하는데 아래 표를 참고하자.

데이터베이스의 각 필드는 Field 클래스의 인스턴스로 표현된다. CharField는 문자(character)필드를 표현하고, DateTimeField 는 날짜와 시간(datetime) 필드를 표현한다. 이 인스턴스들이 각 필드가 어떤 자료형을 가질 수 있는지를 Django 한테 알려준다.

각각의 Field 인스턴스의 이름(question_text 또는 pub_date)은 기계가 읽기 좋은 형식(machine-friendly format)의 데이터베이스 필드 이름이다 이 필드명을 Python 코드에서 사용할 수 있으며, 데이터베이스에서는 컬럼명으로 사용할 것이다.

아래는 튜토리얼에서 설명하고 있는 Field 클래스의 생성자에 첫 번째 위치인수에 대한 내용과 필수적인 인수, ForeignKey 를 사용한 관계설정 내용이다. 잘 읽어보고 이해하자.

Field 클래스의 생성자에 선택적인 첫번째 위치 인수를 전달하여 사람이 읽기 좋은(human-readable) 이름을 지정할 수도 있습니다. 이 방법은 Django 의 내부를 설명하는 용도로 종종 사용되는데, 이는 마치 문서가 늘어나는 것 같은 효과를 가집니다. 만약 이 선택적인 첫번째 위치 인수를 사용하지 않으면, Django 는 기계가 읽기 좋은 형식의 이름을 사용합니다. 이 예제에서는, Question.pub_date 에 한해서만 인간이 읽기 좋은 형태의 이름을 정의하겠습니다. 그 외의 다른 필드들은, 기계가 읽기 좋은 형태의 이름이라도 사람이 읽기에는 충분합니다.

몇몇 Field 클래스들은 필수 인수가 필요합니다. 예를 들어, CharField 의 경우 max_length 를 입력해 주어야 합니다. 이것은 데이터베이스 스키마에서만 필요한것이 아닌 값을 검증할때도 쓰이는데, 곧 보게 될것입니다.

또한 Field 는 다양한 선택적 인수들을 가질 수 있습니다. 이 예제에서는, default 로 하여금 votes 의 기본값을 0 으로 설정하였습니다.

마지막으로, ForeignKey 를 사용한 관계설정에 대해 설명하겠습니다. 이 예제에서는 각각의 Choice 가 하나의 Question 에 관계된다는 것을 Django 에게 알려줍니다. Django 는 다-대-일(many-to-one), 다-대-다(many-to-many), 일-대-일(one-to-one) 과 같은 모든 일반 데이터베이스의 관계들를 지원합니다.

Part 2 - 3. 모델 활성화


Django 는 위에서 models.py 에 입력한 코드로 이 앱을 위한 데이터베이스 스키마를 생성한다(데이터베이스의 CREATE TABLE 문). 또한, Question 과 Choice 객체에 접근하기 위한 파이썬 데이터베이스 접근 API를 생성한다.

참고)
Django 의 앱들은 <꼈다, 뺐다> 를 할 수 있따. 앱을 다수의 프로젝트에서 사용할 수 있으며, 앱을 배포할 수도 있다. 왜냐하면 특정 Django 의 사이트(영역)에 앱들이 묶여있지 않아도 되기 때문이다.

참고에 따라서, 현재 프로젝트에서 polls 앱이 설치되어 있다는 것을 알려야 한다.

앱을 현재의 프로젝트에 포함시키기 위해서는, mysite / settings.py 의 INSTALLED_APPS 설정에 polls 앱의 구성 클래스(class PollsConfig(AppConfig))에 대한 참조를 추가해줘야 한다. 경로를 추가해줘야 한다고 이해할 수 있다.

PollsConfig 클래스polls / apps.py 에 저장되어 있다.

AppConfig 란 앱의 메타데이터를 저장하는 클래스다. 즉. App의 설정이나 실행 일부 등을 저장해둔 클래스이다.

참고)
메타데이터(Meta Data)는 Data에 대한 Data이다. 어떤 목적을 가지고 만들어진 데이터라고도 정의한다. 다른 데이터를 설명해 주는 데이터라고 이해할 수 있다.

이제 mysite / settings.py 의 INSTALLED_APPS 설정을 아래 코드와 같이 편집해주자.

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

여기까지 진행했다면 내 프로젝트는 polls 앱이 포함된 것을 알게 되었다.
앱을 인식시켰으니, 모델의 변경사항을 migration 으로 저장시켜 주자.

아래 명령을 이용하면 변경사항을 저장할 수 있디.
$ python manage.py makemigrations polls

명령을 실행하면 아래와 같은 것이 보일 것이다.

Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice

Migration 은 Django가 모델의 변경사항을 디스크에 저장하는 방법이다.
만약, 모델에 대한 Migration 을 읽기를 원한다면 polls/migrations/0001_initial.py 파일로 저장된 내용을 보면 된다.

이는 위 명령어로 자동적으로 migraion 의 저장을 도와주지만, 수동으로 수정을 원할 때를 대비해서, 사람이 수정할 수 있도록 설계되었기 때문이다.

우선 migration이 내부적으로 어떤 SQL 문장을 실행하는 지 알아보자. sqlmigrate 명령은 migration 이름을 인수로 받아, 실행하고 있는 SQL문장을 보여준다. 아래 명령을 실행해보자.

$ python manage.py sqlmigrate polls 0001

명령을 실행하면 아래와 비슷한 결과를 볼 수 있다(가독성을 위해 줄이 다듬어졌다)

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;

아래는 해당 내용의 참고 사항이다.

  • 위의 출력결과는 사용한 데이터 베이스인 PostgreSQL의 문법을 따른 것이며, 데이터 베이스에 따라 조금씩 다를 수 있다.
  • 테이블 명은 어플리케이션의 이름(polls)와 모델의 소문자 표기의 question와 choice를 합친 형태로 자동 생성된다(이와 같은 작동은 오버라이드(override)로 수정할 수 있다).
  • primary keys(IDs, PK)는 자동적으로 추가된다 (이와 같은 작동은 오버라이드(override)로 수정할 수 있다).
  • 편의상, 장고는 Foreign key(외부키, FK)의 필드명에 _id를 추가한다 (이와 같은 작동은 오버라이드(override)로 수정할 수 있다).
  • Foreign key(외부키) 관계는 Foreign key 라는 제약에 의해 명시 된다. DEFERRABLE에 대해서는 걱정할 필요없다. 이것은 단지 PostgreSQL에서 FK를 트랙잭션 종료까지 강제하지 않도록하기 위해 전달하고 있을 뿐이다.
  • sqlmigrate 명령어는 실제로 데이터베이스에 마이그레이션을 실행하지 않는다. 단지 장고가 필요로 하는 SQL이 무엇인가를 화면에 표시할 뿐이다. 이것을 통해 장고가 무엇을 하려고 하는가 확인하거나, 데이터 베이스 관리자에게 변경을 위한 SQL스크립트 요청 등에 도움이 된다.

이제 migrate를 실행시켜 데이터베이스에 모델과 관련된 테이블을 생성해보자.
아래 명령어를 통해서 데이터베이스의 스키마의 동기화가 이루어진다.

$ python manage.py migrate

migrate 관련한 내용은 후에 더 자세히 다룰 예정이다. 지금 파트에서는 아래 3가지만 기억해놓자.

  • (models.py) 에서 모델을 변경한다.
  • python manage.py makemigrations 를 통해 변경사항에 대한 마이그레이션을 만든다.
  • python manage.py migrate 명령을 통해 변경사항을 데이터베이스에 적용한다.

migration을 만드는 명령과 적용하는 명령이 분리된 것은 VCM(버전 관리 시스템)에 마이그레이션을 보내고, 앱과 함께 나올 수 있또록 하기 위해서이다.

Part 2 - 4. Django API 쉘로 가지고 놀기


API를 파이썬 쉘로 설정하고 놀아보자.
아래 명령어를 실행하자.
python manage.py shell

단순히 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)>]>

영어를 잘 이해하면 각 함수를 사용하는 방법과 이유를 알 수 있다.

q의 데이터베이스를 볼 때 <QuerySet [<Question: Question object (1)>]> 같은 모양은 좋지 않으니 polls.models.py 에서 내용을 수정해주자. 그러면 텍스트 형식으로 나온다. 또한 자동으로 생성하는 관리 사이트에서도 위와 같은 표현이 나오는데 이 때, 편하게 사용하기 위해서다.

from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

추후에 수정시간등을 관리하기 위해서 아래의 내용도 넣어주자(같은 models.py파일에)

import datetime

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


class Question(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

설정을 변경했으면, 다시 manage.py에서 파이썬 쉘을 실행해주자.

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()

ForeignKey 는 어떻게 Database에 저장되고, 실행되는지 아직 파악하지 못했다...계속 검색하고 학습해보자.

Part 2 - 5. Django 관리자


관리자를 생성하는 이유에 대한 공식문서의 글이다. 한번 훑어보자.

관리자를 생성해보자. 아래 캡쳐처럼 따라치면 된다.

후에 서버를 다시 실행하자.
$ python manage.py runserver

웹 브라우저를 열어서 아래 경로로 들어가자.
http://127.0.0.1:8000/admin/

그럼 요런 화면이 나오고 로그인 하면 저런 화면이 나온다.

영어로 나오는 이유는 LANGUAGE_CODE를 설정해 논 값이 영어이기 때문이다. 표시되는 언어를 변경하고 싶으면 해당 내용을 수정하면 된다.

또한 아래에 편집가능한 그룹과 유저들을 보여주는데, 이것들은 django.contrib.auth 모듈에서 제공하는 인증 프레임워크 이다.

polls app이 안보이는 이유는 관리자에게 이 앱이 있다는 것을 알려주지 않았기 때문이다. 알려주자. mysite / admin.py / 에서 아래와 같이 편집해주자.

from django.contrib import admin

from .models import Question

admin.site.register(Question)

그러면 아래처럼 나온다.

Question 을 클릭하면 change list로 이동한다. 이 페이지는 데이터베이스에 저장된 모든 질문들을 보여주며, 그 중 하나를 선택하여 변경할 수 있다. 이전에 등록했던 What's up? 이 있을 것이다. 해당 질문을 클릭해서 수정해보자. 3번째 캡쳐본을 확인하면서 관리자페이지에 대해서 알아보자.

좋은 웹페이지 즐겨찾기