본문 바로가기
DoItDJango

Model

by 자동매매 2023. 4. 12.

장고는 모델(Model)을 이용하여 데이터베이스를 처리한다. 보통 데이터베이스에 데이터를 저장하고 조회하기 위해서 SQL 쿼리문을 이용해야 하지만 장고의 모델(Model)을 사용하면 이런 SQL 쿼리문의 도움없이 데이터를 쉽게 처리할 수 있다.

 

장고 기본 App migrate

데이터베이스가 필요한 앱만 migrate가 필요하다.

 

기본 설치 앱 확인  - admin, auth, contenttypes, sessions 등

[파일명: projects/mysite/config/settings.py]

(... 생략 ...)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
(... 생략 ...)

 

사용하는 데이터베이스에 대한 정보 확인

[파일명: projects/mysite/config/settings.py]

 

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

 

데이터베이스 엔진 :  django.db.backends.sqlite3 

데이터베이스 파일 :  BASE_DIR / db.sqlite3

 

# BASE_DIR :  프로젝트 디렉터리(C:\projects\mysite)

# DB Broswer for SQLite

# 장고의 장점 중 하나는 테이블 작업을 위해 직접 쿼리문을 수행하지 않아도 된다는 점이다. 장고의 ORM(Object Relational Mapping)을 사용하면 쿼리문(데이터베이스의 데이터를 생성, 조회, 수정, 삭제하기 위해 사용하는 질의문(문법))을 몰라도 데이터 작업을 쉽게 할 수 있다.

 

기본 App migrate

(mysite) C:\projects\mysite>python manage.py migrate

 

pybo App 추가

테이블 생성을 위해 가장 먼저 해야 할 일은 pybo 앱을 config/settings.py 파일의 INSTALLED_APPS 항목에 추가하는 일이다.

[파일명: projects/mysite/config/settings.py]

(... 생략 ...)
INSTALLED_APPS = [
    'pybo.apps.PyboConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    (... 생략 ...)
]
(... 생략 ...)

특히 Signals 프레임워크를 사용하여 추가 정보를 추가할 수 있습니다.

 

INSTALLED_APPS에 추가한 pybo.apps.PyboConfig 클래스는 pybo/apps.py 파일에 있는 클래스이다.

[파일명: projects/mysite/pybo/apps.py]

from django.apps import AppConfig


class PyboConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'pybo'

 

모델 작성하기

이제 파이보가 사용할 데이터 모델을 만들어 보자. 파이보는 질문과 답변을 할 수 있는 파이썬 게시판 서비스이다. 따라서 파이보에는 질문과 답변에 해당하는 데이터 모델이 있어야 한다.

 

모델의 속성

그렇다면 질문과 답변 모델에는 어떤 속성들이 필요한지 먼저 생각해 보자. 질문(Question) 모델에는 최소한 다음과 같은 속성이 필요하다.

 

[Question 모델]

속성 설명
subject 질문의 제목
content 질문의 내용
create_date 질문을 작성한 일시

 

[Answer 모델]

속성 설명
question 질문 (어떤 질문의 답변인지 알아야하므로 질문 속성이 필요하다)
content 답변의 내용
create_date 답변을 작성한 일시

 

models.py

이렇게 생각한 속성을 바탕으로 질문(Question)과 답변(Answer)에 해당되는 모델을 pybo/models.py 파일에 정의해 보자.

[파일명: 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()

 

# CharField (글씨제한된 텍스트) / TextField / ForeignKey(기존 모델을 속성으로 연결하려는 경우) 함수

# max_length=200 : 글자 최대길이 제한

# on_delete=models.CASCADE: 답변과 연결된 질문(Question)이 삭제될 경우 답변(Answer)도 함께 삭제된다는 의미

 

model 속성(Field)의 다른 타입 : https://docs.djangoproject.com/en/4.0/ref/models/fields/#field-types

 

테이블 생성하기

이제 작성한 모델을 이용하여 테이블을 생성해 보자.

 

makemigrations

 

모델이 신규로 생성되거나 변경되면 makemigrations 명령을 먼저 수행한 후에 migrate 명령을 수행해야 한다.

python manage.py makemigrations 명령을 수행

(mysite) c:\projects\mysite> python manage.py makemigrations
Migrations for 'pybo':
  pybo\migrations\0001_initial.py
    - Create model Question
    - Create model Answer

(mysite) c:\projects\mysite>

 

위 명령을 수행하면 pybo\migrations\0001_initial.py 라는 파이썬 파일이 자동으로 생성된다.

 

이제 테이블 생성을 위해 다음처럼 migrate 명령을 수행하자.

(mysite) C:\projects\mysite>python manage.py migrate

 

# makemigrations 명령을 수행하더라도 실제로 테이블이 생성되지는 않는다. makemigrations 명령은 장고가 테이블 작업을 수행하기 위한 작업 파일(예: 0001_initial.py)을 생성하는 명령어다.

실제 테이블 작업migrate 명령을 통해서만 가능하다.

 

sqlmigrate

makemigrations로 데이터베이스 작업 파일을 생성하고 migrate 명령을 실행하기 전에 실제 어떤 쿼리문이 실행되는지 sqlmigrate 명령으로 확인해 볼수 있다.

 

sqlmigrate 명령은 단지 실행되는 쿼리만 조회할 뿐이다. 실제 쿼리가 수행되지는 않는다.

(mysite) c:\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;

(mysite) c:\projects\mysite>

python manage.py sqlmigrate pybo 0001 명령에서 "pybo"는 앱 이름을 의미하고 "0001"은 생성된 작업파일(예: 0001_initial.py)의 일련번호를 의미한다.

 

migrate

이제 migrate 명령을 수행하여 실제 테이블을 생성하자.

(mysite) c:\projects\mysite> python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, pybo, sessions
Running migrations:
  Applying pybo.0001_initial... OK

(mysite) c:\projects\mysite>

오류없이 잘 수행되는 것을 확인할 수 있을 것이다. "DB Browser for SQLite"을 이용하여 질문과 답변에 해당되는 테이블들이 잘 생성되었는지 확인해 보도록 하자.

실제 테이블은 pybo_question과 pybo_answer라는 이름으로 생성 되었다. 장고로 코딩을 할 때는 이런 테이블명을 몰라도 된다. 왜냐하면 코딩할 때는 테이블명 대신 Question과 Answer와 같은 모델을 사용하기 때문이다.

 

모델 사용하기

이번에는 모델 사용법을 장고 셸로 익혀보자. 장고 셸은 다음과 같이 실행할 수 있다.

(mysite) c:\projects\mysite> python manage.py shell
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

일반적인 파이썬 셸을 실행하는 것이 아니라 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 모델을 이용하여 질문 데이터를 만들어 보자.

 

>>> from django.utils import timezone
>>> q = Question(subject='pybo가 무엇인가요?', content='pybo에 대해서 알고 싶습니다.', create_date=timezone.now())
>>> q.save()

 

Question 모델의 create_date 속성은 DateTimeField 타입이므로 timezone.now()로 현재일시를 대입하였다. 위처럼 Question 모델의 객체 q를 생성한 후 save함수를 실행하면 질문 데이터가 1건 생성된다.

데이터가 1건 생성되면 반드시 다음처럼 id 값이 생성된다.

 

>>> q.id
1

 

id는 모델 데이터의 유일한 값으로 프라이머리 키(PK:Primary Key)라고도 한다. 이 id 값은 데이터를 생성할 때마다 1씩 증가된다.

다음처럼 2번째 질문을 만들어 보자.

 

>>> q = Question(subject='장고 모델 질문입니다.', content='id는 자동으로 생성되나요?', create_date=timezone.now())
>>> q.save()
>>> q.id
2

 

두번째로 생성한 질문의 id는 예상대로 2라는 것을 알수 있다.

 

Question 조회

이번에는 저장한 데이터를 조회해 보자.

 

>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

 

저장한 Question 모델의 데이터는 Question.objects 를 통해서 조회할 수 있다. Question.objects.all()은 모든 Question 데이터를 조회하는 함수이다. 결과값으로는 QuerySet 객체가 리턴되는데 위처럼 Question 객체를 포함하고 있다. Question object (1), Question object (2) 에서 1과 2는 Question 데이터의 id 값이다.

다음처럼 Question 모델에 __str__ 메서드를 추가하면 id 값 대신 제목을 표시할 수 있다.

[파일명: projects/mysite/pybo/models.py]

 

(... 생략 ...)

class Question(models.Model):
    subject = models.CharField(max_length=200)
    content = models.TextField()
    create_date = models.DateTimeField()

    def __str__(self):
        return self.subject

(... 생략 ...)

 

이렇게 수정하고 Question.objects.all() 함수를 다시 실행 해 보자. 그런데 잠깐, 모델이 변경되었으므로 장고 셸을 재시작해야 변경된 결과를 확인할 수 있다.

장고 셸을 종료하기 위해서는 장고 셸에서 Ctrl+Z 또는 quit()을 입력하면 된다.

(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를 수행할 필요가 없다. makemigrations, migrate 명령이 필요한 경우는 모델의 속성이 변경되었을때 뿐이다.

이번에는 filter를 사용하여 id 값이 1인 Quesiton 데이터를 조회해 보자.

 

>>> Question.objects.filter(id=1)
<QuerySet [<Question: pybo가 무엇인가요?>]>

 

filter는 조건에 해당되는 데이터를 모두 리턴해 주기 때문에 다건을 의미하는 QuerySet이 리턴된다.

id는 유일한 값이므로 filter 대신 get을 이용하여 조회할 수도 있다.

 

>>> Question.objects.get(id=1)
<Question: pybo가 무엇인가요?>

 

get으로 조회할 경우 QuerySet이 아닌 Question 모델 객체가 리턴되었다. filter는 다건을 리턴하지만 get은 한건만 리턴하기 때문이다.

하지만 get으로 조회시 조건에 맞는 데이터가 없으면 다음과 같은 오류가 발생한다.

 

>>> Question.objects.get(id=3)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\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:\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.

 

get은 반드시 1건의 데이터를 조회할 때 사용한다. 보통 get은 id와 같은 유일한 값으로 조회할 경우에만 사용한다.

이번에는 subject에 "장고"라는 문자열이 포함된 데이터만 조회하는 방법에 대해서 알아보자.

 

>>> Question.objects.filter(subject__contains='장고')
<QuerySet [<Question: 장고 모델 질문입니다.>]>

 

subject__contains='장고'의 의미는 "subject에 '장고'라는 문자열이 포함되어 있는가?" 라고 해석할 수 있다. subject__contains 에서 언더바(_)가 1개가 아닌 2개임에 주의하자.

데이터를 조회하는 filter의 사용법은 위에서 알아본 것 외에도 아주 많다. filter에 대한 자세한 사용법은 장고 공식 문서를 참조하도록 하자. 장고 공식 문서는 장고 개발시 필수적으로 참조해야 하는 문서이다.

 

Question 수정

이번에는 저장한 Question 데이터를 수정해 보자. 먼저 다음과 같이 id 값이 2인 데이터를 조회한다.

 

>>> q = Question.objects.get(id=2)
>>> q
<Question: 장고 모델 질문입니다.>

 

그리고 subject 속성을 다음과 같이 수정하자.

 

>>> q.subject = 'Django Model Question'
>>>

 

여기까지만 해서는 수정이 되지 않는다. 반드시 다음처럼 save를 수행해 주어야 변경된 데이터가 반영된다는 것을 꼭 기억하자.

 

>>> q.save()
>>> q
<Question: Django Model Question>

 

Question 삭제

이번에는 id 값이 1인 Question 데이터를 삭제해 보자.

 

>>> q = Question.objects.get(id=1)
>>> q.delete()
(1, {'pybo.Question': 1})

 

delete를 수행하면 해당 데이터가 삭제된다. 삭제될 때는 위와 같이 추가정보가 리턴된다. (1, {'pybo.Question': 1})은 Question 모델이 1개 삭제되었음을 의미한다.

실제로 삭제되었는지 다음처럼 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 조회

답변을 조회하는 방법은 질문과 마찬가지로 Answer의 id 값을 사용하면 된다.

 

>>> a = Answer.objects.get(id=1)
>>> a
<Answer: Answer object (1)>

 

Answer객체인 a를 사용하면 답변에 연결된 질문도 조회할 수 있다.

 

>>> a.question
<Question: Django Model Question>

 

Answer 모델 객체인 a를 통해서 질문을 찾는것은 Answer 모델에 question 속성이 연결되어 있기 때문에 매우 쉽다. 그렇다면 질문을 이용하여 답변을 찾는 것은 가능할까?

가능하다. 다음처럼 하면 된다.

 

>>> q.answer_set.all()
<QuerySet [<Answer: Answer object (1)>]>

 

q.answer_set을 사용하면 질문에 연결된 답변을 가져올 수 있다. Question 모델에는 answer_set 이라는 속성이 없지만 Answer 모델에 Question 모델이 ForignKey로 연결되어 있기 때문에 q.answer_set 과 같은 역방향 접근이 가능하다.

연결모델명_set(예:answer_set)은 상식적으로 생각하면 더 쉽다. 질문 하나에는 여러개의 답변이 가능하므로 q.answer_set이 가능하지만 답변 하나에는 여러개의 질문이 있을 수 없으므로 a.question_set은 불가능하다. 답변 하나에는 질문 하나만 가능하기 때문에 a.question만 가능하다.

정말 신통방통한 장고의 기능이 아닐수 없다. 연결모델명_set 방법은 자주 사용하니 꼭 기억해 두도록 하자.

'DoItDJango' 카테고리의 다른 글

template 사용 - class기반 뷰어 생성  (0) 2023.04.13
데이터 저장  (0) 2023.04.13
URL 별칭  (0) 2023.04.13
조회와 템플릿  (0) 2023.04.13
superuser  (1) 2023.04.13

댓글