본문 바로가기
Research/Django

[Django] 점프 투 장고 튜토리얼 - 03-7. 모델 변경

by RIEM 2021. 11. 24.

Django 점프 투 장고 정리

작성일 : 2021-11-23

문서버전 : 1.0 

개요

이 문서는 점프 투 장고 사이트의 장고 튜토리얼 학습 내용을 정리한 내용입니다.

레퍼런스

점프 투 장고 https://wikidocs.net/72242

3-07. 모델 변경

현재 화면은 아래와 같다. 게시글 작성자의 ID가 표시되지 않는 상황이다. Question과 Answer 모델에 ‘글쓴이’ 관련 데이터인 author 속성을 추가해보자.

 

Question 속성 추가

Question 모델에 author 속성 추가하기

> ../projects/mysite/pybo/models.py

 
from django.db import models
from django.contrib.auth.models import User


class Question(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    (... 생략 ...)

Author 필드는 User를 ForeignKey로 적용하여 변수 선언했다. User 모델은 django.contrib.auth 앱이 제공하는 사용자 모델이다. on_delete=models.CASCADE는 계정이 삭제될 시 해당 작성자가 작성한 질문도 모두 삭제하라는 의미다. Author 속성에 저장되는 사용자 객체는 로그인 후에 request 객체로 얻는다.

 

모델을 변경했으니 반드시 makemigrations과 migrate를 통해 DB 상태를 변경해주자. 


> makemigrations

(mysite) c:\projects\mysite>python manage.py makemigrations
You are trying to add a non-nullable field 'author' to question without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 

Makemigrations를 실행하면 위와 같은 option 선택 문구가 나타난다.

 

Question모델에 author를 추가하면 이미 등록된 게시물에 author 필드값이 함께 추가가 되어야하는데, 현재 장고가 author 값을 알지 못하는 상황이다. 이 문제를 해결하기 위해 author에 어떤 값을 저장할지 묻는 상황이다.

 

첫 번째 옵션은 author 속성을 null로 설정하는 방법, 두 번째 옵션은 기존 게시물에 추가될 author에 임의의 계정 정보를 강제로 추가하는 방법이다. 질문, 답변에는 author값이 무조건 있어야 하기 때문에 두 번째 방법으로 강제 추가하자.

 

위에서 옵션 1을 선택해주자.

Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>>     

 

파이썬 셸이 가동되어 ‘>>>’프롬프트가 표시된다. 여기서 1을 다시 입력하라.

>>> 1
Migrations for 'pybo':
  pybo\migrations\0002_question_author.py
    - Add field author to question

(mysite) c:\projects\mysite> 

 

입력한 ‘1’은 admin 계정의 id값이다. 따라서 기존 게시물의 author에는 admin 계정이 등록된다. 계정 생성시마다 id가 1부터 순차적으로 증가한다. Createsuperuser로 최초 생성한 계정 ‘admin’의 id값은 1이다.


> migrate 명령으로 변경 내용 DB에 적용하기

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

 

Answer 속성 추가

Question 모델과 같은 방법으로 Answer 모델에 author 속성을 추가하자.

> ../projects/mysite/pybo/models.py

 
(... 생략 ...)

class Answer(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    (... 생략 ...)

 

Makemigrations 명령 실행하면 Question 모델과 동일한 선택 창이 나타난다. 동일하게 옵션 1을 선택하고 admin id값을 ‘1’로 입력해준다.

(mysite) c:\projects\mysite>python manage.py makemigrations
You are trying to add a non-nullable field 'author' to answer without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 1
Migrations for 'pybo':
  pybo\migrations\0003_answer_author.py
    - Add field author to answer


> migrate 명령 실행

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

(mysite) c:\projects\mysite> 

 

만약, Author 속성에 null을 허용하고 싶다면 아래와 같이 null=True 지정하면 된다.

Author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)

 

null=True 속성을 부여할 경우 migrate시 DB에 null 허용 칼럼으로 생성된다.

 

Author 저장

모델 Question, Answer에 author 속성이 추가되었다. 따라서 질문, 답변을 저장할 때도 author 속성도 함께 저장해야 한다. 즉, 질문과 답변에 글쓴이의 정보를 추가하는 것이다.

> ../projects/mysite/pybo/views.py

 
def answer_create(request, question_id):
    (... 생략 ...)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.author = request.user  # author 속성에 로그인 계정 저장
            (... 생략 ...)
    (... 생략 ...)

 

Request.user은 현재 로그인한 계정의 User 모델 객체다. 답변 작성한 글쓴이는 현재 로그인한 계정이므로 answer.author = request.user 로 처리했다.


> ../projects/mysite/pybo/views.py

Question_create 함수도 마찬가지로 수정해주자.

 
def question_create(request):
    (... 생략 ...)
        if form.is_valid():
            question = form.save(commit=False)
            question.author = request.user  # author 속성에 로그인 계정 저장
    (... 생략 ...)

 

글쓴이도 현재 로그인한 계정이므로 question.author = request.user로 처리했다. 

 

로그인이 필요한 함수

단, 로그아웃 상태에서 질문 또는 답변을 할 경우 아래와 같은 ValueError가 발생한다.

Request.user에 User객체가 들어와야 하는데 AnonymousUser 객체가 들어와서 발생한 오류다. 앞서 우리는 author 속성 정의 시 User객체를 이용하라고 정의하여 answer.author = request.user에서 User 대신 AnonymousUser가 대입되어 오류가 발생한 것이다. 

 

이 문제 해결을 위해선 request.user를 사용하는 함수에 @login_required 라는 애너테이션을 사용한다. 이 애너테이션은 로그인이 필요한 함수를 의미한다.

> ../projects/mysite/pybo/views.py

 
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm, AnswerForm
from django.core.paginator import Paginator
from django.contrib.auth.decorators import login_required

(... 생략 ...)

@login_required(login_url='common:login')
def answer_create(request, question_id):
    (... 생략 ...)

@login_required(login_url='common:login')
def question_create(request):
    (... 생략 ...)

 

Answer_create와 question_create 함수는 함수 내에서 request.user을 사용하는 로그인이 필요한 함수다. 이를 위해 @login_required 어노테이션을 사용해야 한다. 만약 로그아웃 상태에서 @login_required 어노테이션이 적용된 함수가 호출될 경우 자동으로 로그인 화면으로 이동해준다. @login_required 어노테이션은 위 코드에서 볼 수 있듯이 login_url=’common:login’처럼 로그인URL을 지정할 수 있다.

 

로그아웃된 상태에서 답변을 등록하려 하면 로그인 사이트가 나타난다.

 

댓글