모델에 필드 추가

jurin·2020년 12월 27일
0

플라스크 - python

목록 보기
14/17

책 '점프 투 플라스크'를 공부하면서 정리한 내용입니다.
출처 : 점프 투 플라스크

Question 모델 필드 추가

1. Question 모델에 user_id, user 필드 추가

models.py파일 속 Question 모델에 ‘작성자’ 필드를 추가

user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
    user = db.relationship('User', backref=db.backref('question_set'))

user_id 필드 : User 모델 데이터의 id값을 Question 모델에 포함시키기 위한 필드
user 필드 : Question 모델에서 User 모델을 참조하기 위한 필드

db.relationship 함수의 backref 매개변수는 User 모델 데이터를 통해 Question 모델 데이터를 참조하려고 설정

qustion.user.username 처럼 Question 모델 객체 question을 통해 User 모델 데이터를 참조할 수 있다.

나중에 자신이 작성한 질문을 user.question_set으로 참조할 수 있다.

db.ForeignKey는 다른 모델과 연결하는 것을 의미하므로 ondelete='CASCADE'는 이 질문과 연결되어 있는 User 모델 데이터가 DB 명령으로 삭제되면 Question 모델 데이터도 함께 삭제될 수 있게 해주는 설정

모델을 수정했으므로 flask db migrate 명령으로 리비전 파일 생성하고 flask db upgrade 명령으로 리비전 파일 적용

(myproject) c:\projects\myproject>flask db migrate
(myproject) c:\projects\myproject>flask db upgrade

이러면 오류가 발생한다. user_id 필드가 Null을 허용하지 않기 때문이다. 앞서 모델에서 user_id 필드를 만들 때 nullable 설정을 False로 지정했다. user_id 필드를 만들기 전 실습을 하면서 DB에 여러 Question 모델 데이터를 저장했다. 그 데이터들에는 user_id 값이 없기 때문에 오류가 발생한 것이다.

flask db upgrade 명령 오류 해결하기

앞으로 nullable 설정이 False인 필드를 추가할 때 어쩔수 없이 해야만 한다.

1. user_id의 nullable 설정을 False 대신 True로 바꾸고 user_id를 임의의 값으로 설정(여기서는 1로 설정)
user_id 필드의 nullable=False를 nullable=True로 변경하고 이미 저장된 데이터의 user_id 필드의 기본값을 1로 설정하기 위해 server_default='1'을 입력

user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=True, server_default='1')

server_default에 지정한 '1'은 최초로 생성한 User 모델 데이터의 id를 의미.

필드의 기본값 설정 방법 server_default vs default
server_default : flask db upgrade 명령을 수행할 때 필드를 갖고 있지 않던 기존 데이터에도 기본값이 저장
default : 새로 생성되는 데이터에만 기본값을 생성

2. flask db migrate 명령으로 리비전 오류 확인
다시 오류가 발생 -> 이전의 migrate 명령은 제대로 수행되었지만 upgrade를 실패하여 정상으로 종료되지 않았기 때문

다음 명령으로 migrate 작업의 최종 리비전 확인

(myproject) c:\projects\myproject>flask db heads

다음 명령으로 migrate 작업이 정상적으로 완료된 리비전 확인

(myproject) c:\projects\myproject>flask db current

이 두개가 서로 같지 않은것을 확인할 수 있다. 따라서 현재 리비전을 최종 리비전으로 변경해야 한다.

(myproject) c:\projects\myproject>flask db stamp heads

이후 다시 확인

(myproject) c:\projects\myproject>flask db current

최종 리비전(head) == 현재 리비전(current)

3. flask db migrate 명령, flask db upgrade 명령 다시 실행하기
잘 수행됐다면 DB에는 Question 모델 데이터 모두 user_id 필드에 '1' 저장

4. user_id의 nullable 설정을 다시 False로 변경하기
이제 다시 Question 모델을 기존 코드로 바꾼다.

user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)

Answer 모델 필드 추가

1. user_id 필드에 nullable=True 설정

user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=True, server_default='1')
    user = db.relationship('User', backref=db.backref('answer_set'))

2. migrate, upgrade 명령 수행

3. Answer 모델 다시 수정하고 migrate, upgrade 명령 수행

질문, 답변 등록 시 user 필드 추가

답변 등록 뷰 수정

answer_views.py에 Answer 모델 객체 answer을 생성할 때 user = g.user 추가

from flask import Blueprint, url_for, request, render_template, g

answer = Answer(content=content, create_date=datetime.now(), user=g.user)

g.user는 auth_views.py 파일의 @bp.before_app_request 애너테이션으로 만든 로그인한 사용자 정보

질문 등록 뷰 수정

from flask import Blueprint, render_template, request, url_for, g

question = Question(subject=form.subject.data, content=form.content.data, create_date=datetime.now(), user=g.user)

이제 로그인을 하면 질문이 등록되고 안하면 오류가 뜬다.

로그인 상태가 필요한 함수 추가

로그아웃 상태로 질문을 등록하면 오류가 생기는데 g.user의 값이 None이기 때문이다. 로그아웃 상태에서 질문 또는 답변을 등록할 때 사용자를 로그인 페이지로 redirect해야 한다. -> 질문, 답변 등록 함수의 시작 부분에 리다이렉트를 처리하기 위한 코드 추가 but 이 방식은 코드 중복, 비효율적 이기 때문에 '파이썬 데코레이터'로 처리

데코레이터 함수 생성

auth_views.py 파일에 login_required라는 이름의 데코레이터 함수를 생성

import functools

def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))
        return view(**kwargs)
    return wrapped_view

이제 다른 함수에 @login_required 애너테이션을 지정하면 login_required 데코레이터 함수가 먼저 실행된다.

@login_required 애너테이션 적용

  • question_views.py
from pybo.views.auth_views import login_required

@bp.route('/create/', methods=('GET', 'POST'))
@login_required
  • answer_view.py
from .auth_views import login_required

@bp.route('/create/<int:question_id>', methods=('POST',))
@login_required

로그아웃 상태에서 답변 등록 불가능하게 만들기

답변을 등록할 때 로그인 화면으로 이동하도록 만들어 놓기는 했지만 처음부터 글 작성을 할 수 없게 한다.

답변 작성 템플릿 수정

question_detail.html에서 textarea 엘리먼트가 로그인 상태가 아닌 경우 disabled를 지정하여 입력 자체를 하지 못하도록 설정

<textarea {% if not g.user %}disabled{% endif %}
                    name="content" id="content" class="form-control" rows="10"></textarea>

profile
anaooauc1236@naver.com

0개의 댓글

관련 채용 정보