Django 점프 투 장고 정리
작성일 : 2021-11-09
문서버전 : 1.0
개요
이 문서는 점프 투 장고 사이트의 장고 튜토리얼 학습 내용을 정리한 내용입니다.
레퍼런스
점프 투 장고 https://wikidocs.net/72242
2-2. 모델
장고는 모델(Model)로 DB 처리를 한다. 일반적으로 DB에서 데이터를 저장하고 조회하기 위해 SQL 쿼리문을 이용해야 하지만 장고의 모델(Model)을 사용하면 이런 SQL 쿼리문을 사용ㅎ지 않고 데이터를 쉽게 처리할 수 있다.
2-2-1. 장고 앱 migrate
개발 서버 실행 명령어를 입력 시 나오는 문구를 자세히 살펴보자.
(mysite) c:\Users\yangs\Desktop\django_jump\projects\mysite>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
November 10, 2021 - 15:31:19
Django version 3.2.9, using settings 'config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
18개의 적용되지 않는 migration들이 있다. Admin, auth, contenttypes, sessions 앱들을 위해 migration들을 적용하지 않으면 프로젝트가 제대로 작성하지 않는다고 한다. 이를 위해 ‘python manage.py migrate’를 하라고 친절히 알려준다.
참고로 admin, auth, contenttypes, sessions들은 config/settings.py에서 확인할 수 있다.
> config/settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
...
위에서는 Messages, staticfiles 앱들은 언급되지 않았는데 DB와 무관한 앱이기 때문이다. 즉, DB가 필요한 앱만 migration이 필요하다. 좀 더 내려가면 DB 정보도 있다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
DB 엔진은 django.db.backends.sqlite3가 default로 정의되어 있다. 그리고 DB 파일은 BASE_DIR 디렉토리 하위에 db.sqlite3 파일에 저장한다고 정의되었다. 여기서 BASE_DIR 는 프로젝트 디렉터리를 의미한다.
우리의 프로젝트 BASE_DIR은 ..projects/mysite이다.
SQLite는 주로 개발용 또는 소규모 프로젝트에서 사용되는 가벼운 파일 기반 DB다. 개발 시 SQLite 사용해 빠르게 개발하고 실제 운영시스템은 규모있는 DB를 사용하는 것이 일반적이다.
경고 문구에서 안내했듯이 앱들이 필요로 하는 테이블을 생성해주자. 테이블은 DB에서 데이터를 저장하기 위한 데이터 집합의 모임이다.
> migrate
(mysite) C:\Users\yangs\Desktop\django_jump\projects\mysite>python manage.py migrate
Result
DB Browser for SQLite
SQLite의 GUI 도구인 “DB Browser for SQLite” 설치하면 DB의 테이블들을 확인할 수 있다.
장고의 장점 중 하나는 테이블 작업을 위해 직접 쿼리문을 수행하지 않아도 된다는 점이다. 쿼리문이란 데이터베이스의 데이터를 생성, 조회, 수정, 삭제하기 위해 사용하는 질의문(문법)이다. 다행히도 장고의 ORM(Object Relational Mapping)을 사용하여 쿼리문을 몰라도 데이터 작업을 쉽게 할 수 있다.
ORM이란?
전통적으로 DB를 사용하는 프로그램들은 DB의 데이터를 조회하거나 저장하기 위해 쿼리문을 사용한다. 오늘날에도 많이 사용하지만, 개발자마다 만드는 쿼리문이 상이하고, 잘못 작성될 경우 시스템 성능 저하가 발생할 수 있다. 그리고 DB를 MySQL에서 오라클로 변경하면 프로그램에서 사용한 쿼리문을 새 DB의 규칙에 맞게 수정해야한다는 어려움이 있다.
ORM(Object Relational Mapping)을 사용하면 DB의 테이블을 모델화하여 사용하기 때문에 위에서 열거한 단점을 해소할 수 있다. ORM을 사용하면 통일된 쿼리문을 작성할 수 있다. DB 종류가 바뀌어도 쿼리문이 아닌 모델을 사용하기 때문에 프로그램 수정 필요가 없다.
모델 작성하기
Pybo는 질문과 답변을 할 수 있는 파이썬 게시판 서비스다. 이를 위해 질문과 답변에 해당하는 데이터 모델이 필요하다.
모델의 속성
모델에는 어떤 속성이 필요할까?
질문(Question) 모델의 속성
- Subject : 질문 제목
- Content : 질문 내용
- Create_date : 질문 작성 일자
답변(Answer) 모델의 속성
- Question : 답변과 매칭되는 질문
- Content : 답변 내용
- Create_date : 답변 작성 일자
Models.py
위 속성들을 기반으로 models를 정의해보자.
> ../projects/mysite/pybo/models.py
from django.db import models
class Question(models.Model):
subject = models.CharField(max_length=200)
content = models.TextField()
create_date = models.DateTimeField()
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
content = models.TextField()
create_date = models.DateTimeField()
Question 모델을 보면 subject, content, create_date 속성이 있다. 제목은 최대 200자까지 가능하도록 max_length=200으로 설정했다. 제목과 같이 글자수가 제한된 텍스트는 CharField를 사용한다. 내용(Content)처럼 글자수 제한할 수 었는 경우 TextField를 사용한다. 작성일시와 같이 날짜와 시간과 관련된 속성은 DateTimeField를 사용한다.
Answer 모델의 경우 질문에 대한 답변이기 때문에 Question 모델을 속성으로 가져와야 한다. ForeignKey를 사용해서 다른 모델을 속성으로 연결할 수 있다. on_delete=models.CASCADE는 해당 답변과 연결된 질문(Question)이 삭제될 경우 답변(Answer)도 함께 삭제하라는 의미다. 장고에는 다양한 속성(Field)가 있으니 아래에서 찾아보면 좋다. https://docs.djangoproject.com/en/3.0/ref/models/fields/#field-types
Pybo 앱 등록
작성한 모델을 이용해 테이블을 생성해보자.
> ../projects/mysite/config/settings.py
테이블 생성을 위해 pybo앱을 우선 config/settings.py 파일 내 INSTALLED_APP 항목에 추가해주자.
...
INSTALLED_APPS = [
'pybo.apps.PyboConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
...
INSTALLED_APPS에 추가한 pybo.apps.PyboConfig 클래스는 pybo/apps.py에 있는 PyboConfig 클래스다. Pybo 앱 생성 시 자동으로 만들어진 파일이다. 해당 앱을 INSTALLED_APPS 항목에 추가하지 않으면 DB 관련 작업이 불가하다.
Makemigrations
> Migrate
(mysite) C:\Users\yangs\Desktop\django_jump\projects\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
No migrations to apply.
Your models in app(s): 'pybo' have changes that are not yet reflected in a migration, and so won't be applied.
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
Migrate 명령을 했으나 정상적으로 진행되지 않았다. 모델이 신규로 생성되거나 변경될 경우 makemigrations 명령을 먼저 수행해준 뒤 migrate 명령을 수행해줘야 하기 때문이다.
> makemigrations
(mysite) C:\Users\yangs\Desktop\django_jump\projects\mysite>python manage.py makemigrations
Migrations for 'pybo':
pybo\migrations\0001_initial.py
- Create model Question
- Create model Answer
Makemigrations 명령을 수행하면 pybo/migrations/0001_initial.py 라는 파이썬 파일이 자동으로 생성된 것을 알 수 있다. 즉, makemigrations 명령은 장고가 테이블 작업을 수행하기 위한 작업 파일인 0001_initial.py를 생성하는 명령어다. 실제 테이블 생성은 migrate 명령으로 한다.
Sqlmigrate
Makemigrations으로 DB 작업 파일 생성하고 migrate 명령을 실행하면 된다. 그전에 실제 어떤 쿼리문이 실행되는지 sqlmigrate 명령으로 조회해보자
(mysite) C:\Users\yangs\Desktop\django_jump\projects\mysite>python manage.py sqlmigrate pybo 0001
BEGIN;
--
-- Create model Question
--
CREATE TABLE "pybo_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "subject" varchar(200) NOT NULL, "content" text NOT NULL, "create_date" datetime NOT NULL);
--
-- Create model Answer
--
CREATE TABLE "pybo_answer" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "content" text NOT NULL, "create_date" datetime NOT NULL, "question_id" bigint NOT NULL REFERENCES "pybo_question" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "pybo_answer_question_id_e174c39f" ON "pybo_answer" ("question_id");
COMMIT;
python manage.py sqlmigrate pybo 0001명령에서 pybo는 앱 이름이고 0001은 생성된 작업파일(ex, 0001_initial.py)의 일련번호를 의미한다.
SQL문에 익숙한 독자라면 DB에 어떤 일들이 벌어질지 유추할 수 있을 것이다. 여기에 출력되는 SQL 문들이 무엇인지 몰라도 상관없다. 장고는 SQL 대신 모델을 사용하기 때문이다.
Migrate
Migrate 명령으로 실제 테이블을 생성하자.
DB Browser for SQLite에서 Pybo_question과 pybo_anwswer라는 이름으로 생성된 것을 확인했다.
모델 사용하기
이번엔 모델 사용법을 배워보자. 장고 shell을 사용하면 된다.
input
Python manage.py shell
Result
일반적인 파이썬 셸을 실행하는 것이 아니고 python manage.py shell처럼 장고 셸을 실행해야 한다. 장고 셸은 장고에 필요한 환경들이 자동으러 설정되어 실행된다.
Question 생성
Question과 Answer 모델은 장고 셸에서 다음처럼 import하여 사용할 수 있다.
>>> from pybo.models import Question, Answer
>>> from django.utils import timezone
>>> q = Question(subject='pybo가 무엇인가요?', content='pybo에 대해서 알고 싶습니다.', create_date=timezone.now())
>>> q.save()
Question 모델의 create_date 속성은 DateTimeField 타입인데 timezone.now()로 현재일시를 대입하면 된다. 위와 같이 객체 q 생성 후 save 함수를 실행하면 질문 데이터가 1건 생성되는 방식이다.
>>> q.id
1
Id는 모델 데이터의 유일한 값인 PK(Primary Key)다. 이 id 값은 데이터 생성 시 1씩 증가한다.
자, 2번째 질문을 만들어 보자.
>>> q = Question(subject='장고 모델 질문입니다.', content='id는 자동으로 생성되나요?', create_date=timezone.now())
>>> q.save()
>>> q.id
2
Question 조회
저장한 데이터를 조회해보자.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
Question 모델의 데이터는 Question.objects를 통해 조회할 수 있다. Question.objects.all()은 모든 Question 데이터를 조회하는 함수다. 결과값으로 QuerySet 객체가 반환되는데 위의 경우 2개의 Question 객체를 포함하고 있다. Question object (1)에서 1은 Question 데이터의 id 값이다.
> ../projects/mysite/pybo/models.py
아래와 같이 Question 모델에 __str__ 메서드를 추가하여 id 값 대신 제목을 표시할 수도 있다.
...
class Question(models.Model):
subject = models.CharField(max_length=200)
content = models.TextField()
create_date = models.DateTimeField()
def __str__(self):
return self.subject
...
위와 같이 모델이 수정되었기 때문에 재확인 하기 위해선 장고 셸을 다시 시작해야한다.
> 장고 Shell에서 Question.objects.all() 함수 실행
(mysite) c:\projects\mysite>python manage.py shell
>>> from pybo.models import Question, Answer
>>> Question.objects.all()
<QuerySet [<Question: pybo가 무엇인가요?>, <Question: 장고 모델 질문입니다.>]>
>>>
1, 2 id 값 대신 질문 제목이 표시되는 것을 확인할 수 있다.
모델에 메서드 추가될 경우 makemigrations와 migrate 수행할 필요가 없다. Migrate 명령어는 모델의 속성이 변경되었을 때만 수행하기 때문이다.
> filter 사용해 id 값이 1인 Question 데이터 조회하기
Filter는 조건에 해당되는 데이터를 모두 리턴해주며 QuerySet을 리턴한다.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: pybo가 무엇인가요?>]>
Filter는 복수의 건을 리턴하지만 get은 1건만 리턴한다.
> get 사용해 id 값이 1인 Question 데이터 조회하기
Id는 유일한 값이라 filter 대신 get으로 조회할 수 있다. Get은 QuerySet이 아닌 Question 모델 객체가 리턴된다.
>>> Question.objects.get(id=1)
<Question: pybo가 무엇인가요?>
조건에 맞지 않는 데이터 조회시 아래와 같이 나타난다.
>>> Question.objects.get(id=3)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\yangs\Desktop\django_jump\venvs\mysite\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\yangs\Desktop\django_jump\venvs\mysite\lib\site-packages\django\db\models\query.py", line 435, in get
raise self.model.DoesNotExist(
pybo.models.Question.DoesNotExist: Question matching query does not exist.
>>>
>>> Question.objects.filter(subject__contains='장고')
<QuerySet [<Question: 장고 모델 질문입니다.>]>
> 특정 문자열 포함 데이터 조회하기
subject__contains=’장고’는 subject에 장고라는 문자열 포함되어 있는지를 해석한다.
Question 수정
Question 데이터를 수정해보자.
> 우선 id 값이 2인 데이터 조회하기
>>> q = Question.objects.get(id=2)
>>> q
<Question: 장고 모델 질문입니다.>
>>> q.subject = 'Django Model Question'
여기에서 save() 까지 수행해주어야 수정 데이터가 반영된다.
> subject 속성을 수정하기
>>> q.save()
>>> q
<Question: Django Model Question>
Question 삭제
> id 값이 1인 Question 데이터 삭제하기
>>> q = Question.objects.get(id=1)
>>> q.delete()
(1, {'pybo.Question': 1})
Delete로 데이터를 삭제했다. 데이터 삭제 시 추가정보가 함께 리턴된다.
Question.objects.all() 명령어로 조회해보니 실제로 삭제된 것을 재확인할 수 있다.
>>> Question.objects.all()
<QuerySet [<Question: Django Model Question>]>
Answer 작성
답변 데이터를 생성해보자.
>>> q = Question.objects.get(id=2)
>>> q
<Question: Django Model Question>
>>> from django.utils import timezone
>>> a = Answer(question=q, content='네 자동으로 생성됩니다.', create_date=timezone.now())
>>> a.save()
답변 데이터 만들기 위해 질문이 필요하므로 id가 2인 질문을 먼저 조회한 후 question 속성에 대입해주었다.
Answer 모델도 Question 모델과 마찬가지로 유일한 값을 의미하는 id가 자동으로 생성된다.
>>> a.id
1
Answer 조회
답변을 조회해보자.
>>> a = Answer.objects.get(id=1)
>>> a
<Answer: Answer object (1)>
Answer객체인 a를 사용해 답변에 연결된 질문 조회하기
>>> a.question
<Question: Django Model Question>
역으로 질문을 이용해 답변을 찾기
>>> q.answer_set.all()
<QuerySet [<Answer: Answer object (1)>]>
Q.answer_set을 사용하면 질문에 연결된 답변 가져올 수 있다. Question 모델에는 answer_set 속성은 없지만 Answer 모델에 Question 모델이 ForeignKey로 연결되어 있기 때문에 q.answer_set과 같은 역방향 접근이 가능하다.
연결모델명_set(ex, answer_set)은 상식적으로 생각하면 더 쉽다. 질문 하나에는 여러 개의 답변이 가능하므로 q.answer_set이 가능하지만 답변 하나에는 여러개의 질문이 있을 수 없으므로 a.question_set은 불가능하다. 답변 하나에는 질문 하나만 가능하기 때문에 a.question만 가능하다.
'Research > Django' 카테고리의 다른 글
[Django] 점프 투 장고 튜토리얼 - 02-4. 조회와 템플릿(template) (0) | 2021.11.11 |
---|---|
[Django] 점프 투 장고 튜토리얼 - 02-3. 장고 관리자(Admin) (0) | 2021.11.11 |
[Django] 점프 투 장고 튜토리얼 - 02-1. URL과 VIEW (0) | 2021.11.10 |
[Django] 점프 투 장고 튜토리얼 - 01. 장고 개발 준비 (0) | 2021.11.10 |
[Django] 공식 튜토리얼 3 정리 (0) | 2021.11.05 |
댓글