[Flask] 3-8 모델 수정하기

2023. 12. 2. 22:48Web/Flask

728x90

회원가입, 로그인, 로그아웃 기능이 완성돼서 질문, 답변을 "누가" 작성했는지 알 수 있게 됐다

이제 기능을 조금씩 다듬어서 완벽하게 만들어 보자

여기서는 Question, Answer 모델을 수정하여 "글쓴이"에 해당하는 user 속성을 추가할 것이다

 

SQLite 설정 수정하기

SQLite 데이터베이스는 ORM을 사용할 때 몇 가지 문제점이 있다

이것은 SQLite 데이터베이스에만 해당하

PostgreSQL이나 MySQL 등의 다른 데이터베이스와는 상관없는 내용이다

 

pybo/__init__.py 파일을 다음과 같이 수정해보자

[파일명: projects/myproject/pybo/__init__.py]

이와 같이 수정하면 데이터베이스의 프라이머리 키, 유니크 키, 인덱스 키 등의 이름이 변경되므로

flask db migrate 명령과 flask db upgrade 명령으로 데이터베이스를 변경해야 한다

 

Question 모델에 글쓴이 추가하기

아래와 같이 글쓴이 정보를 Question 모델에 추가하기 위해 user_id, user 속성을 추가하자

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

user_id 속성은 User 모델을 Question 모델과 연결하기 위한 속성이고

user 속성은 Question 모델에서 User 모델을 참조하기 위한 속성이다

 

리비전 파일 생성하기

모델을 수정했으니 flask db migrate 명령을 실행해서 리비전 파일을 생성해보자

 

리비전 파일 적용하기

flask db upgrade 명령을 수행하자

그런데 오류가 발생했다

그 이유는 "user_id 속성이 Null을 허용하지 않기 때문"이라고 한다

Question 모델 데이터를 여러 건을 저장했었는데

 그 데이터에는 user_id 속성의 값이 없다

변경된 모델은 이를 허용하지 않으므로 오류가 발생한 것이다

기존에 이미 저장되어 있던 데이터들 때문에 발생한 오류인듯하다

 

flask db upgrade 명령 오류 해결하기

이 문제를 해결하려면 아래와 같은 과정을 거쳐야 한다

어떤 모델에 nullable=Flase 인 속성을 추가하고자 할 때 어쩔수 없이 거져야 하는 과정이다

 

1. user_id의 nullable 설정을 False 대신 True로 바꾸기

2. user_id를 임의의 값으로 설정하기(여기서는 1로 설정)

3. flask db migrate 명령, flask db upgrade 명령 다시 실행하기

4. user_id의 nullable 설정을 다시 False로 변경하기

5. flask db migrate 명령, flask db upgrade 명령 다시 실행하기

 

nullable=True

우선 user_id 속성의 nullable=False nullable=True로 변경해보자

그리고 user_id 속성의 기본값을 1로 설정하기 위해 server_default='1'을 입력하자

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

여기서 server_default에 지정한 '1'은 최초로 생성한 User 모델 데이터의 id 값을 의미한다

기존에 저장된 Question 데이터의 user_id값을 설정하기 위해서 server_default='1'이라는 속성을 추가했다

 

리비전 오류 확인하기

이제 다시 flask db migrate 명령을 수행해 보자

하지면 여전히 오류가 발생한다

이유는 이전의 migrate 명령은 제대로 수행되었지만 upgrade를 실패하여

정상으로 종료되지 않았기 때문이다

 

최종 리버전

flask db heads 명령으로 현재 migrate 작업의 최종 리비전을 확인해 보자

 

현재 리버전

flask db current 명령으로 현재 시점의 리비전을 확인해 보자

결과를 보면 알겠지만 "현재 시점의 리비전"과 "최종 리비전"이 같지 않다

2개의 리비전이 다른 이유는 migrate 이후 upgrade를 실패했기 때문이다

때문에 migrate 명령을 수행할 수 없는 것이다

둘이 일치해야 migrate 작업을 진행할 수 있다

 

현재 리버전을 최종 리버전으로 변경하기

앞에서 설명했듯 migrate 작업을 계속 진행하려면 flask db stamp heads 명령을 사용하여

현재 리비전을 최종 리비전으로 되돌려야 한다

리비전을 변경한 뒤 다시 flask db current 명령을 수행하자

현재(current) 리비전이 최종(head) 리비전으로 되돌려진 것을 확인할 수 있다

 

migrate, upgrade 다시 입력하기

다시 flask db migrate 명령과 flask db upgrade 명령을 입력해 보자

오류 없이 잘 동작한다

이제 데이터베이스에는 Question 모델 데이터 모두 user_id 속성에 '1'이 저장된다

 

nullable=False

이제 비로소 Question 모델의 user_id 속성을 nullable=True에서 nullable=False로 변경할수 있다

그리고 server_default는 필요하지 않으므로 제거하자

코드를 수정하고  flask db migrateflask db upgrade 명령을 입력하자

지금까지 이미 데이터베이스에 저장된 데이터와 이와 관련된

모델이 변경되었을 때 처리하는 방법을 알아보았다

여기서는 모델 속성에 nullable=False를 설정하는 방법을 알아보았다

migrate 명령을 사용하다 보면 뭔가 꼬이는 현상이 종종 발생한다

이럴 때는 현재 데이터베이스의 변경 내역을 자세히 봐야한다

 

 Answer 모델에 글쓴이 추가하기

Answer 모델도 같은 방법으로 user_id 속성을 추가하자

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

user_id 속성에 nullable=True를 설정했다

실습하면서 nullable=False 지정은 고생길이라는 것을 훤히 알았을 것이므로

nullable=True, server_default='1'이라고 설정하여 기본값을 저장한 다음에 진행하자

 

 migrate 명령과 upgrade 명령을 입력하자

Answer 모델에서 server_default는 제거하고 nullable=False 변경하자

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

다시  migrate, upgrade 명령을 순서대로 입력하자

오류없이 잘 처리될 것이다

 

답변 등록

답변 등록시 글쓴이를 저장하자

[파일명: projects/myproject/pybo/views/answer_views.py]

g.user는 세션에 저장된 사용자 정보 데이터이다

 

질문 등록

질문 등록도 똑같은 방법으로 수정하자

[파일명: projects/myproject/pybo/views/question_views.py]

이제 로그인 후에 질문과 답변을 등록해 보자

문제 없이 잘 등록될 것이다

 

login_required

로그아웃 상태에서 질문 또는 답변을 등록하면 아래와 같은 오류가 발생한다

오류가 발생한 이유는 로그아웃 상태에서는 g.user의 값이 None이기 때문이다

이 문제를 해결하려면 로그아웃 상태에서는 질문 또는 답변을 작성하려고 할 때 로그인을 먼저 진행할 수 있도록 로그인 페이지로 리다이렉트해야 한다

그렇게 하려면 모든 질문, 답변 등록 함수의 시작 부분에 세션 값을 체크하여

사용자 정보가 없을 경우 로그인 페이지로 리다이렉트하는 코드를 추가해야 한다

이런식으로 코드를 작성하면 코드가 중복되므로 비효율적이다

이것 해결하는 방법이 있다

 

@login_required 데코레이터

auth_views.py 파일에 login_required라는 이름의 데코레이터 함수를 아래와 같이 만들어 보자

[파일명: projects/myproject/pybo/views/auth_views.py]
[파일명: projects/myproject/pybo/views/auth_views.py]

코드에서 보듯 데코레이터 함수는 기존 함수를 감싸는 방법으로 간단히 만들 수 있다

이제 라우팅 함수에 @login_required 애너테이션을 지정하면 login_required 데코레이터 함수가 먼저 실행될 것이다

login_required 함수는 g.user가 있는지를 조사하여 없으면

로그인 URL로 리다이렉트 하고 g.user가 있으면 원래 함수를 그대로 실행할 것이다

 

요청 방식이 GET인 경우에는 로그인 후에 원래 가려던 페이지로 다시 찾아갈수 있도록

로그인 페이지에 next 파라미터를 전달했다

따라서 로그인 함수도 next 파라미터를 처리하기 위해 다음과 같이 수정해야 한다

[파일명: projects/myproject/pybo/views/auth_views.py]

로그인시 next 파라미터 값이 있으면 읽어서 로그인 후

해당 페이지로 이동하고 없으면 메인 페이지로 이동하게 했다

 

@login_required 적용하기

로그인이 필요한 함수에 @login_required 데코레이터를 적용해 보자

우선 질문 등록 함수에 적용

[파일명: projects/myproject/pybo/views/question_views.py]

답변을 등록하는 함수에도 적용

[파일명: projects/myproject/pybo/views/answer_views.py]

이제 로그아웃 상태에서 질문 또는 답변 등록을 시도하면 로그인 화면으로 리다이렉트될 것이다

 

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

현재 질문 등록은 로그아웃 상태에서는 아예 글을 작성할 수 없어서 만족스럽다

하지만 답변 등록은 로그아웃 상태에서도 글을 작성할 수 있다

물론 답변 작성 후 <저장하기>를 누르면 자동으로 로그인 화면으로

이동되므로 큰 문제는 아니지만 작성한 답변이 사라지는 문제가 있다

 

작성한 글이 사라지는 문제를 해결하려면 로그아웃 상태에서는 아예 답변 작성을 못하게 막는 것이 좋을 것이다

답변 작성 템플릿에서 textarea 엘리먼트가 로그인 상태가 아닌 경

disabled를 지정하여 입력 자체를 하지 못하도록 아래와 같이 수정하자

[파일명: projects/myproject/pybo/templates/question/question_detail.html] Copy

이제 disabled 설정으로 textarea 엘리먼트가 비활성화될 것이다

728x90