01. 모델 속성 구상하기
질문과 답변 모델에는 어떤 속성이 있어야 할까?
질문 모델에는 다음 속성이 필요하다.
속성명 | 설명 |
id | 질문 데이터의 고유 번호 |
subject | 질문 제목 |
content | 질문 내용 |
create_date | 질문 작성일시 |
답변 모델은 다음과 같은 속성이 필요하다.
속성명 | 설명 |
id | 답변 데이터의 고유 번호 |
question_id | 질문 데이터의 고유 번호(어떤 질문에 달린 답변인지 알아야 하므로 질문 데이터의 고유 번호가 필요하다) |
content | 답변 내용 |
create_date | 답변 작성일시 |
02. 질문 모델 생성하기
이렇게 구상한 속성을 바탕으로 모델을 정의해 보자.
먼저 pybo 디렉터리에 models.py 파일을 생성하고 질문 모델인 Question 클래스를 작성하자.
Question 클래스는 모든 모델의 기본 클래스인 db.Model을 상속받았다.
이때 db는 __init__.py 파일에서 생성한 SQLAlchemy 객체다.
질문 모델은 고유 번호(id), 제목(subject), 내용(content), 작성일시(create_date) 속성으로 구성했으며, 각 속성은 db.Column 클래스를 사용해 생성했다.
db.Column 클래스에 어떤 값들을 전달했는지 살펴보면서 각 속성의 특징을 확인해 보자.
먼저 속성을 생성할 때 db.Column() 괄호 안의 첫 번째 인수는 데이터 타입을 의미한다. 데이터 타입은 속성에 저장할 데이터의 종류를 결정한다.
id = db.Column(db.Integer, primary_key=True)
db.Column에는 데이터 타입 외에 여러 속성을 지정할 수 있다.
먼저 고유 번호 id에 지정한 primary_key는 id 속성을 기본 키로 지정한다. 고유 번호는 모델에서 각 데이터를 구분하는 유효한 값으로 중복되면 안 된다.
그리고 nullable은 속성에 빈값을 허용할 것인지를 결정한다. nullable을 지정하지 않으면 해당 속성은 기본으로 빈값을 허용한다. 따라서 속성에 빈값을 허용하지 않으려면 nullable=False를 지정해야 한다.
03. 답변 모델 생성하기
이어서 같은 파일에 답변 모델인 Answer 클래스도 작성하자.
답변 모델에서 id와 content, create_date 속성은 질문 모델의 id, content, create_date와 의미와 목적이 같다.
다른 속성은 question_id와 question인데 두 속성이 왜 필요하고 어떤 의미인지 살펴보자.
question_id 속성은 질문 모델과 연결하려고 추가했다. 답변 모델은 어떤 질문에 대한 답변인지 표시해야 하므로 2단계에서 생성한 질문 모델과 연결된 속성을 포함해야 한다. 이처럼 어떤 속성을 기존 모델과 연결하려면 db.ForeignKey를 사용해야 한다.
question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete='CASCADE'))
db.ForeignKey에 지정한 첫 번째 값 'question.id'는 Question 모델의 id 속성을 의미한다. (※ 실제 전달되는 값은 테이블명.컬럼명 이다.) 두 번째 ondelete에 지정한 값은 삭제 연동 설정이다. 즉, 답변 모델의 question_id 속성은 질문 모델의 id 속성과 연결되며 ondelete='CASCADE'에 의해 데이터베이스에서 쿼리를 이용하여 질문을 삭제하면 해당 질문에 달린 답변도 함께 삭제된다.
그 다음 question 속성은 답변 모델에서 질문 모델을 참조하기 위해 추가했다. 예를 들어 답변 모델 객체에서 질문 모델 객체의 제목을 참조하려면 answer.question.subject처럼 할 수 있다. 이렇게 하려면 속성을 추가할 때 db.Column이 아닌 db.relationship을 사용해야 한다.
question = db.relationship('Question', backref=db.backref('answer_set'))
db.relationship에 지정한 첫 번째 값은 참조할 모델명이고 두 번째 backref에 지정한 값은 역참조 설정이다. 역참조란 쉽게 말해 질문에서 답변을 참조하는 것을 의미한다. 한 질문에는 여러 개의 답변이 달릴 수 있는데 역참조는 이 질문에 달린 답변을 참조할 수 있게 한다. 예를 들어 어떤 질문에 해당하는 객체가 a_question이라면 a_question.answer_set와 같은 코드로 해당 질문에 달린 답변을 참조할 수 있다.
모델을 구상하고 생성했으므로 플라스크의 Migrate 기능을 이용해 데이터베이스 테이블을 생성해 보자.
01. 모델 가져오기
앞선 실습에서 생성한 모델들을 플라스크의 Migrate 기능이 인식할 수 있도록 pybo/__init__.py 파일을 수정하자.
from . import models
02. 데이터베이스 변경을 위한 리비전 파일 생성하기
Question과 Answer 모델을 추가했으므로 데이터베이스가 변경되도록 명령 프롬프트에서 flask db migrate 명령을 수행하자.
flask db migrate
이 명령을 수행하면 18634a293520_.py처럼 데이터베이스 변경을 처리할 리비전 파일이 생성된다.
03. 데이터베이스 갱신하기
이어서 flask db upgrade 명령으로 리비전 파일을 실행하자.
이 과정에서 데이터베이스에 모델 이름과 똑같은 question과 answer라는 이름의 테이블이 생성된다. 지금까지 설명한 1~3단계를 제대로 수행했다면 projects/myproject 디렉터리에 pybo.db 파일이 생성되었을 것이다. pybo.db가 바로 SQLite 데이터베이스의 데이터 파일이다.
pybo.db 데이터 파일에 정말로 question과 answer 테이블이 생성되었는지 확인해 보자. 여기서는 SQLite의 GUI 도구인 DB Browser for SQLite를 사용한다.
01. DB Browser for SQLite 설치하기
https://sqlitebrowser.org/dl 에 접속한 다음 DB Browser for SQLite(이하 DB 브라우저) 설치 파일(standard installer)을 내려받아 설치를 진행하자. 설치 시 체크 옵션에 주의하자.
02. DB브라우저에 pybo.db 열기
윈도우 바탕화면이나 프로그램 메뉴에서 방금 설치한 DB 브라우저를 실행하고 메뉴에서 [파일 → 데이터베이스 열기]를 선택한다. 그리고 앞선 실습에서 생성한 C:/projects/myproject/pybo.db 데이터베이스 파일을 선택하고 <열기>를 누른다.
테이블 목록을 보면 question, answer 테이블이 생성되었음을 확인할 수 있다. DB 브라우저를 종료하자.
모델도 만들었고 모델을 기반으로 테이블도 생성했으니 이제 모델을 사용할 차례다. 여기서는 프로젝트에서 모델을 본격적으로 사용하기 전에 ‘플라스크 셸’이라는 도구를 사용해 모델 사용법을 간단히 알아볼 것이다.
01. 플라스크 셸 실행하기
플라스크 셸은 명령 프롬프트에서 flask shell 명령으로 실행한다.
flask shell
02. 질문 데이터 저장하기
다음 명령을 수행해 Question과 Answer 모델을 플라스크 셸에 불러오자. 그런 다음 Question 모델 객체를 하나 생성해 보자.
Question 모델의 create_date 속성은 DateTime 유형이므로 datetime.now함수로 현재 일시를 대입했다. 객체 q를 만들었다고 해서 데이터베이스에 저장되는 것은 아니다. 데이터베이스에 저장하려면 다음처럼 SQLAlchemy의 db 객체를 사용해야 한다.
코드에서 보듯 신규 데이터를 저장할 때는 add 함수를 사용한 다음 commit 함수까지 실행해야 한다. db.session은 데이터베이스와 연결된 세션, 즉 접속된 상태를 의미한다. 데이터베이스를 처리하려면 이 세션이 필요하다. 세션을 통해서 데이터를 저장, 수정, 삭제 작업을 한 다음에는 반드시 db.session.commit 함수로 커밋을 해주어야 한다.
여기서 주의할 점은 커밋은 취소할 수 없다는 것이다.
커밋은 일종의 ‘결정사인’ 역할을 한다고 생각하면 이해하기 쉽다. 그래서 수행한 작업을 취소하려면 커밋 이전(세션 작업 도중)에 진행해야 한다. 이때 작업을 취소하고 싶으면 db.session.rollback 함수로 되돌리기(롤백)를 실행하면 된다.
데이터가 잘 생성되었는지 확인해 보자.
id는 Question 모델의 기본 키다. id는 앞에서 모델을 생성할 때 설정했던대로 데이터를 생성할 때 속성값이 자동으로 1씩 증가한다. 정말 그런지 두 번째 질문 데이터를 생성한 후 id를 확인해 보자.
결과를 보면 두 번째 질문 데이터의 id는 예상대로 2가 출력됐다.
03. 데이터 조회하기
이번에는 데이터베이스에 저장된 데이터를 모두 조회해 보자.
Question.query.all 함수로 모든 데이터베이스에 저장된 질문 데이터를 조회했다. 이 함수는 Question 객체 리스트를 반환한다. 결과에서 보이는 숫자 1과 2는 Question 객체의 id 속성값이다. 이번에는 filter 함수를 이용해 첫 번째 질문 데이터만 조회하자.
filter 함수는 인자로 전달한 조건에 맞는 데이터를 모두 반환해 준다. 여기서는 기본 키인 id를 이용했으므로 값을 1개만 반환한다.
id는 유일한 값이므로 filter 함수 대신 get 함수를 이용해 조회할 수도 있다.
Question.query.filter(Question.id==1).all()
Question.query.get(1)
다만 get 함수로 조회하면 리스트가 아닌 Question 객체 1개만 반환된다. 이번에는 filter와 like 함수로 subject 속성에 ‘플라스크’라는 문자열이 포함된 데이터를 조회해 보자
‘플라스크’라는 문자열이 포함된 두 번째 질문이 반환되었음을 확인할 수 있다. filter 함수에 전달한 Question.subject.like('%플라스크%') 코드의 의미는 Question 모델 subject 속성에 ‘플라스크’라는 문자열이 포함되었는가?이다. 이때 like 함수에 전달한 문자열에 붙은 % 표기는 다른 문자열을 포함하는지를 나타낸다.
- 플라스크%: ‘플라스크’로 시작하는 문자열
- %플라스크: ‘플라스크’로 끝나는 문자열
- %플라스크%: ‘플라스크’를 포함하는 문자열
04. 데이터 수정하기
이번에는 질문 데이터를 수정해 보자. 데이터를 수정할 때는 단순히 대입 연산자를 사용하면 된다.
두 번째 질문 데이터를 조회한 다음 subject 속성을 수정했다. 앞에서 설명했듯이 데이터를 변경한 후에는 반드시 커밋을 수행해야 데이터베이스에 반영된다.
05. 데이터 삭제하기
이어서 데이터를 삭제하는 것도 실습해 보자. 여기서는 첫 번째 질문을 삭제하자.
첫 번째 데이터를 조회한 다음 delete 함수를 이용해 삭제했다. 이 역시도 커밋을 수행해 데이터베이스에 데이터 삭제 작업을 반영했다. 이어서 실제로 데이터베이스에서 첫 번째 질문이 삭제되었는지 확인하자.
첫 번째 질문이 삭제되어서 두 번째 질문만 조회되었다.
06. 답변 데이터 생성 후 저장하기
이번에는 답변(Answer) 데이터를 생성하고 저장해 보자.
답변 데이터를 생성하려면 질문 데이터가 필요하므로 우선 질문 데이터를 구해야 한다. id가 2인 질문 데이터를 가져온 다음 q에 저장했다. 그런 다음 Answer 모델의 question 속성에 방금 가져온 q를 대입해 답변 데이터를 생성했다.
Answer 모델에는 어떤 질문에 해당하는 답변인지 연결할 목적으로 question_id 속성이 있다. Answer 모델의 객체를 생성할 때 question에 q를 대입하면 question_id에 값을 지정하지 않아도 자동으로 입력되어 편리하다.
Answer도 Question 모델과 마찬가지로 id 속성이 기본 키이므로 값이 자동으로 생성된다.
다음 명령으로 id값을 확인해 보고 이 값을 이용해 데이터도 확인해 보자.
07. 답변에 연결된 질문 찾기 vs 질문에 달린 답변 찾기
앞에서 구성한 Answer 모델의 question 속성을 이용하면 ‘답변에 연결된 질문’을 조회할 수 있다.
답변에 연결된 질문 찾기는 Answer 모델에 question 속성이 정의되어 있어서 매우 쉽다. 그런데 반대의 경우도 가능할까? 즉, 질문에서 답변을 찾을수 있을까?
Question 모델과 Answer 모델은 현재 연결된 상태이고, Answer 모델의 question 속성에 역참조 설정 backref=db.backref(‘answer_set’)을 적용했다. 그러므로 이를 사용하면 질문과 연결된 답변을 쉽게 가져올 수 있다.
지금 여러분은 역참조 설정의 유용함을 별로 느끼지 못할 것이다. 하지만 이 기능은 개발자에게 큰 편의를 가져다주는 신통방통한 녀석이다. 앞으로도 자주 사용할 예정이므로 꼭 기억해 두자. 이제 플라스크 셸을 종료하자.
플라스크 셸에서 빠져 나오려면 <Ctrl+Z>를 누르고 <Enter>를 입력한다.
'백엔드' 카테고리의 다른 글
API 개발하기(미니터 : 트윗) (0) | 2021.08.07 |
---|---|
API 개발하기(미니터 : 회원가입) (0) | 2021.08.07 |
플라스크 ORM 라이브러리 사용하기 (0) | 2021.08.06 |
ORM(object relational mapping) (0) | 2021.08.06 |
블루프린트 라우트 함수 관리 (0) | 2021.08.04 |