2-1장 플라스크 개발 기초 공사!

1. 플라스크 기초 다지기

플라스크 프로젝트 구조 살펴보기

├── pybo/
│      ├─ __init__.py
│      ├─ models.py
│      ├─ forms.py
│      ├─ views/
│      │   └─ main_views.py
│      ├─ static/
│      │   └─ style.css
│      └─ templates/
│            └─ index.html
└── config.py

pybo.py 파일은 pybo 패키지로 변경할 수 있다. 이 구조에서 보듯 pybo 디렉터리 안에 있는 __init__.py 파일이 pybo.py 파일의 역할을 대신할 것이다.

패키지 안에 구성해야 하는 파일과 디렉토리는 다음과 같다.

  • 데이터베이스를 처리하는 models.py 파일
    파이보 프로젝트는 ORM(object relational mapping)을 지원하는 파이썬 데이터베이스 도구인 SQLAlchemy를 사용한다. SQLAlchemy는 모델 기반으로 데이터베이스를 처리한다.
    파이보 프로젝트에는 "모델 클래스들을 정의할 models.py 파일이 필요하다"

  • 서버로 전송된 폼을 처리하는 forms.py 파일
    파이보 프로젝트는 웹 브라우저에서 서버로 전송된 폼을 처리할 때 WTForms라는 라이브러리를 사용한다. WTForms 역시 모델 기반으로 폼을 처리한다. 그래서 폼 클래스를 정의할 forms.py 파일이 필요하다.

  • 화면을 구성하는 views 디렉터리
    pybo.py 파일에 작성했던 hello_pybo 함수의 역할은 화면 구성이었다. views 디렉터리에는 바로 이런 함수들로 구성된 뷰 파일들을 저장한다.

  • CSS, 자바스크립트, 이미지 파일을 저장하는 static 디렉터리
    static 디렉터리는 파이보 프로젝트의 스타일시트(.css), 자바스크립트(.js) 그리고 이미지 파일(.jpg, .png) 등을 저장한다.

  • HTML 파일을 저장하는 templates 디렉터리
    templates 디렉터리에는 파이보의 질문 목록, 질문 상세(question_list.html, question_detail.html) 등의 HTML 파일을 저장한다.

  • 파이보 프로젝트를 설정하는 config.py 파일
    config.py 파일은 파이보 프로젝트의 환경을 설정한다. 파이보 프로젝트의 환경변수, 데이터베이스 등의 설정을 이 파일에 저장한다.

2. 플라스크 애플리케이션 팩토리

app = Flask(__name__)

플라스크는 app 객체를 사용해 여러가지 설정을 진행하는데,이와 같은 방식으로 app 객체를 전역으로 사용하면 프로젝트 규모가 커질수록 문제가 발생할 확률이 높아짐.
이문제를 해결하기 위해서 플라스크는 공식적으로 "애플리케이션 팩토리(application factory)를 사용하라"고 권한다.

애플리케이션 팩토리 사용하기

1) pybo.py를 __init__.py 파일로 변경하기

먼저 디렉터리를 하나 만든다.

(myproject) c:\projects\myproject> mkdir pybo

그리고 move 명령어로 pybo.py 파일을 pybo/__init__.py 파일로 대체하고 플라스크 서버를 실행한다.

(myproject) c:\projects\myproject> move pybo.py pybo/__init__.py
         1개 파일을 이동했습니다.
(myproject) c:\projects\myproject>flask run
 * Serving Flask app "pybo" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 202-513-618
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

플라스크 서버가 이전과 똑같이 잘 실행되는 것을 확인할 수 있다. 1장에서 플라스크의 기본앱을 set FALSK_APP=pybo 로 설정했다. 따라서 이전에 pybo는 프로젝트 루트에 있는 pybo.py 파일을 가리켰지만 이번에는 pybo 모듈 즉 pybo/__init__.py 파일을 가리킨다. 따라서 pybo.py 파일을 pybo/__init__.py 파일로 대체했지만 FLASK_APP 환경변수를 변경하지 않고서도 오류없이 정상으로 동작한다.

2) 애플리케이션 팩토리 사용하기

__init__.py 파일을 열고 create_app 함수를 선언하는 방식으로 코드를 수정하자.

from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.route('/')
    def hello_pybo():
        return 'Hello, Pybo!'

    return app

create_app 함수가 app 객체를 생성해 반환하도록 코드를 수정했다. 이때 app 객체가 함수 안에서 사용되므로 hello_pybo 함수를 create_app 함수 안에 포함했다. 바로 여기서 사용된 create_app 함수가 애플리케이션 팩토리다.

*함수명으로 create_app 대신 다른 이름을 사용하면 정상으로 동작하지 않는다. create_app은 플라스크 내부에서 정의된 함수명이다.

3. 블루프린트로 라우트 함수 관리하기

pybo/__init__.py 파일 내용을 보면 create_app 함수안에 hello_pybo 함수가 들어 있다. hello_pybo 함수는 URL에서 / 에 매핑되는 함수인데, 그 매핑을 @app.route('/')라는 애너테이션이 만들어 준다. 이때 @app.route와 같은 애너테이션으로 매핑되는 함수를 라우트 함수라고 한다.

그런데 지금까지 작성한 대로라면 새로운 URL이 생길 때 라우트 함수를 create_app 함수 안에 계속 추가해야 하는 불편함이 있다. 이때 사용할 수 있는 클래스가 블루프린트(Blueprint)다.

블루프린트 사용하기

플라스크의 블루프린트를 이용하면 라우트 함수를 구조적으로 관리할 수 있다.
블루프린트(blueprint)는 "청사진"을 뜻하는데, 플라스크에서는 URL과 호출되는 함수의 관계를 확인할 수 있는 Blueprint 클래스를 의미한다.

1) 블루프린트 생성하기

pybo 디렉터리 아래에 views 디렉터리를 만든다.

(myproject) c:\projects\myproject> cd pybo
(myproject) c:\projects\myproject\pybo> mkdir views

views 디렉터리에 main_views.py 파일을 만들어 다음과 같이 작성

from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix='/')


@bp.route('/')
def hello_pybo():
    return 'Hello, Pybo!'

pybo/__init__.py 파일에 있던 hello_pybo 함수를 main_views.py 파일로 옮긴 것.
애너테이션이 @app.route --> @bp.route로 변경되었다.
@bp.route에서 bp는 Blueprint 클래스로 생성한 객체를 의미한다.
Blueprint 클래스로 객체를 생성할 때는 'main'(이름), __name__(모듈명), url-prefix='/'(URL 프리픽스) 값을 전달해야 한다.

*블루프린트 객체의 이름인 'main'은 나중에 함수명으로 URL을 찾아주는 url_for 함수에서 사용된다.


  • URL 프리픽스는 접두어 URL을 정할 때 사용한다

URL 프리픽스는 특정 파일(main_views.py)에 있는 함수의 애너테이션 URL 앞에 기본으로 붙일 접두어 URL을 의미한다. 예를 들어 main_views.py 파일의 URL 프리픽스에 url_prefix='/' 대신 url_prefix='/main'이라고 입력했다면 hello_pybo 함수를 호출하는 URL은 localhost:5000/이 아니라 localhost:5000/main/이 된다. URL 프리픽스는 나중에 좀 더 자세히 알아보자.


2) 플라스크 앱 생성 시 블루프린트 적용하기

1단계에서 생성한 블루프린트 파일을 적용하기 위해 __init__.py 파일을 다음과 같이 수정한다.

from flask import Flask

def create_app():
    app = Flask(__name__)

    from .views import main_views
    app.register_blueprint(main_views.bp)

    return app

reate_app 함수에 등록되었던 hello_pybo 함수 대신 블루프린트를 사용하도록 변경.

3) 라우트 함수 등록하기

main_views.py 파일을 열어 hello_pybo 함수의 URL 매핑을 /에서 /hello로 바꾸고, index 함수를 추가해 URL 매핑을 / 로 입력하자.

from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix='/')


@bp.route('/hello')
def hello_pybo():
    return 'Hello, Pybo!'


@bp.route('/')
def index():
    return 'Pybo index'

4) 라우트 함수 동작 확인하기

웹 브라우저를 열고 localhost:5000과 localhost:5000/hello에 접속하여 각 url로 접속할 때 웹서버가 제대로 동작하는지 확인한다.

*아마도 명령 프롬프트의 현재 디렉터리는 pybo일 것이다. 플라스크 서버는 myproject 디렉터리에서 실행해야 제대로 동작하므로 cd .. 명령으로 myproject 디렉터리로 이동한 후 flask run 명령을 실행하자. 플라스크 서버 실행 위치를 다시 한번 기억하자.

localhost:5000에 접속하면 라우터 /에 매핑된 index 함수가 호출되어 "Pybo index"가 출력되고, localhost:5000/hello에 접속하면 라우터 /hello에 매핑된 hello_pybo 함수가 호출되어 "Hello, Pybo!"가 출력된다.

4. 모델로 데이터 처리하기

웹 서비스는 데이터를 처리할 때 대부분 데이터베이스를 사용한다. 그런데 데이터베이스를 사용하려면 SQL 쿼리(query)라는 구조화된 질의를 작성하고 실행하는 등의 복잡한 과정이 필요하다. 이때 ORM(obeject relational mapping)을 이용하면 파이썬 문법만으로도 데이터베이스를 다룰 수 있다. 즉, ORM을 이용하면 개발자가 쿼리를 직접 작성하지 않아도 데이터베이스의 데이터를 처리할 수 있다.

데이터베이스를 쉽게 사용할 수 있게 해주는 ORM 알아보기

다음과 같은 형태로 구성된 질문 테이블에 데이터를 입력한다고 가정해 보자.


idsubjectcontent
1안녕하세요가입인사드립니다^^
2질문 있습니다ORM이 궁금합니다
.........

SQL 쿼리와 ORM을 비교해 볼때 question 테이블에 삽입하는 쿼리는 보통 다음과 같다.


[쿼리를 이용한 새 데이터 삽입 예]

insert into question (subject, content) values ('안녕하세요', '가입 인사드립니다 ^^');
insert into question (subject, content) values ('질문 있습니다', 'ORM이 궁금합니다');

하지만 ORM을 사용하면 쿼리 대신 파이썬 코드로 다음처럼 작성할 수 있다.


[ORM을 이용한 새 데이터 삽입 예]

question1 = Question(subject=’안녕하세요’, content='가입 인사드립니다 ^^')
db.session.add(question1)
question2 = Question(subject=’질문 있습니다’, content='ORM이 궁금합니다')
db.session.add(question2)

코드에서 Question은 파이썬 클래스이며, 이처럼 데이터를 관리하는 데 사용하는 ORM 클래스를 모델이라고 한다. 모델을 사용하면 내부에서 SQL 쿼리를 자동으로 생성해 주므로 직접 작성하지 않아도 된다. 즉, 파이썬만 알아도 데이터베이스에 질의할 수 있다.


ORM의 장점을 더 알아보자

ORM을 이용하면 데이터베이스 종류에 상관 없이 일관된 코드를 유지할 수 있어서 프로그램을 유지·보수하기가 편리하다. 또한 내부에서 안전한 SQL 쿼리를 자동으로 생성해 주므로 개발자가 달라도 통일된 쿼리를 작성할 수 있고 오류 발생률도 줄일 수 있다.


플라스크 ORM 라이브러리 사용하기

1) ORM 라이브러리 설치하기

Flask-Migrate 라이브러리를 설치하면 SQLAlchemy도 함께 설치되므로 myproject 가상 환경에서 다음 명령을 수행하여 Flask-Migrate 라이브러리를 설치한다.

(myproject) c:\projects\myproject>pip install Flask-Migrate
Collecting Flask-Migrate
Using cached Flask_Migrate-2.5.3-py2.py3-none-any.whl (13 kB)
Requirement already satisfied: Flask>=0.9 in c:\venvs\myproject\lib\site-packages (from Flask-Migrate) (1.1.2)
Collecting Flask-SQLAlchemy>=1.0
Downloading Flask_SQLAlchemy-2.4.3-py2.py3-none-any.whl (17 kB)
Processing c:\users\pahke\appdata\local\pip\cache\wheels\70\08\70\cea787a7e95817b831469fa42af076046e55a05f7c94657463\alembic-1.4.2-py2.py3-none-any.whl
... 생략 ...
Successfully installed Flask-Migrate-2.5.3 Flask-SQLAlchemy-2.4.3 Mako-1.1.3 SQLAlchemy-1.3.17 alembic-1.4.2 python-dateutil-2.8.1 python-editor-1.0.4 six-1.15.0


*만약 아래와 같은 오류가 났을 경우

File "c:\venvs\myproject\lib\site-packages\flask_sqlalchemy\__init__.py", line 914, in apply_driver_hacks
    sa_url.database = os.path.join(app.root_path, sa_url.database)
AttributeError: can't set attribute


다음처럼 SQLAlchemy의 버전을 다운그레이드 해야한다.

(myproject) c:\projects\myproject>pip install SQLAlchemy==1.3.23

2) 설정 파일 추가하기

파이보에 ORM을 적용하려면 config.py라는 설정 파일이 필요하다. 루트 디렉터리에 config.py 파일을 생성하고 다음과 같은 코드를 작성한다.

import os

BASE_DIR = os.path.dirname(__file__)

SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'pybo.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False

지금 설정한 내용은 pybo.db라는 데이터베이스 파일을 프로젝트의 루트 디렉터리에 저장하려는 것이다.
SQLALCHEMY_DATABASE_URI는 데이터베이스 접속 주소이고 SQLALCHEMY_TRACK_MODIFICATIONS는 SQLAlchemy의 이벤트를 처리하는 옵션이다. 이 옵션은 파이보에 필요하지 않으므로 False로 비활성화한다.


SQLite는 어떤 데이터베이스일까?

파이썬 기본 패키지에 포함된 SQLite는 주로 소규모 프로젝트에서 사용하는 가벼운 파일을 기반으로 한 데이터베이스다. 보통은 SQLite로 개발을 빠르게 진행하고 이후 실제 운영 시스템에 반영할 때에는 좀 더 규모가 큰 데이터베이스를 사용한다.


3) ORM 적용하기

pybo/__init__.py 파일을 수정해 SQLAlchemy를 적용한다.

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

import config

db = SQLAlchemy()
migrate = Migrate()


def create_app():
  app = Flask(__name__)
  app.config.from_object(config)

  # ORM
  db.init_app(app)
  migrate.init_app(app, db)

  # 블루프린트
  from .views import main_views
  app.register_blueprint(main_views.bp)

  return app

config.py 파일에 작성한 항목을 app.config 환경 변수로 부르기 위해 app.config.from_object(config) 코드를 추가한다. 그리고 전역 변수로 db, migrate 객체를 만든 다음 create_app 함수 안에서 init_app 메서드를 이용해 초기화한다.

db 객체를 create_app 함수 안에서 생성하면 블루프린트와 같은 다른 모듈에서 불러올 수 없기 때문에 db, migrate와 같은 객체를 create_app 함수 밖에서 생성하고, 실제 객체 초기화는 create_app 함수에서 init_app 함수를 통해 진행한다.

4) 데이터베이스 초기화하기

flask db init 명령으로 데이터베이스를 초기화한다.

(myproject) c:\projects\myproject>flask db init
Creating directory c:\projects\myproject\migrations ...  done
Creating directory c:\projects\myproject\migrations\versions ...  done
Generating c:\projects\myproject\migrations\alembic.ini ...  done
Generating c:\projects\myproject\migrations\env.py ...  done
Generating c:\projects\myproject\migrations\README ...  done
Generating c:\projects\myproject\migrations\script.py.mako ...  done
Please edit configuration/connection/logging settings in 'c:\\projects\\myproject\\migrations\\alembic.ini' before proceeding.

이 명령은 데이터베이스를 관리하는 초기 파일들을 migrations라는 디렉터리에 자동으로 생성해 준다. 이때 생성되는 파일들은 Flask-Migrate 라이브러리에서 사용된다. 그리고 데이터베이스를 초기화하는 flask db init 명령은 최초 한 번만 수행하면 된다.


데이터베이스 관리 명령어 정리하기

명령어설명
flask db migrate모델을 새로 생성하거나 변경할 때 사용 (실행하면 작업파일이 생성된다.)
flask db upgrade모델의 변경 내용을 실제 데이터베이스에 적용할 때 사용 (위에서 생성된 작업파일을 실행하여 데이터베이스를 변경한다.)

이밖에도 명령어가 있지만 특별한 경우가 아니라면 이 2가지를 주로 사용한다.
명령어 종류를 확인할땐 flask db를 입력하면 된다.


모델 만들기

*모델은 데이터를 다룰 목적으로 만든 파이썬 클래스이다.

1) 모델 속성 구상하기

질문과 답변 모델에 필요한 속성

[질문 모델 속성]

속성명설명
id질문 데이터의 고유 번호
subject질문 제목
content질문 내용
create_date질문 작성일시

[답변 모델 속성]

속성명설명
id답변 데이터의 고유번호
question_id질문 데이터의 고유 번호(어떤 질문에 달린 답변인지 알아야 하므로 질문 데이터의 고유 번호가 필요하다)
content답변 내용
create_date답변 작성일시

2) 질문 모델 생성하기

구상한 속성을 바탕으로 모델을 정의한다.
pybo 디렉터리에 models.py 파일을 생성하고 질문 모델인 Question 클래스를 작성한다.

from pybo import db

class Question(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  subject = db.Column(db.String(200), nullable=False)
  content = db.Column(db.Text(), nullable=False)
  create_date = db.Column(db.DateTime(), nullable=False)

Question 클래스는 모든 모델의 기본 클래스인 db.Model을 상속받았다. 이때 db는 __init__.py 파일에서 생성한 SALAlchemy 객체다. 질문 모델은 고유번호(id), 제목(subject), 내용(content), 작성일시(create_date)속성으로 구성했으며, 각 속성은 db.Column으로 생성했다.

db.Column() 괄호 안의 첫 번째 인수는 데이터 타입을 의미한다. 데이터 타입은 속성에 저장할 데이터의 종류를 결정한다.

  • db.Integer -> 고유 번호와 같은 숫자값
  • db.String -> 제목처럼 글자 수가 제한된 텍스트
  • db.Text -> 글 내용처럼 글자 수를 제한할 수 없는 텍스트
  • db.Datetime -> 날짜와 시각

*primary_key

id 속성을 기본 키로 지정한다. 기본 키로 지정하면 중복된 값을 가질수 없게 된다. 고유 번호는 모델에서 각 데이터를 구분하는 유효한 값으로 중복되면 안된다.

* 데이터베이스에서는 id와 같은 특징을 가진 속성을 기본 키(primary key)라고 한다. 플라스크는 데이터 타입이 db.integer이고 기본키로 지정한 속성은 값이 자동으로 증가하는 특징도 있어서 데이터를 저장할 때 해당 속성값을 지정하지 않아도 1씩 자동으로 증가하여 저장된다.

*nullable

nullable은 속성에 빈값을 허용할 것인지를 결정한다. nullable을 지정하지 않으면 해당 속성은 기본으로 빈값을 허용한다. 따라서 속성에 빈값을 허용하지 않으려면 nullable=False를 지정해야 한다.

3) 답변 모델 생성하기
이어서 같은 파일에 답변 모델인 Answer 클래스도 추가한다.

from pybo import db

class Question(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  subject = db.Column(db.String(200), nullable=False)
  content = db.Column(db.Text(), nullable=False)
  create_date = db.Column(db.DateTime(), nullable=False)

class Answer(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete='CASCADE'))
  question = db.relationship('Question', backref=db.backref('answer_set'))
  content = db.Column(db.Text(), nullable=False)
  create_date = db.Column(db.DateTime(), nullable=False)

답변 모델에서도 id, content, create_date의 속성은 질문 모델과 같다.

*question_id

question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete='CASCADE'))

question_id 의 속성은 질문 모델과 연결하기 위해 추가했다. 답변 모델 데이터는 어떤 질문에 대한 답변인지 알아야 하므로 이전에 생성한 질문 모델과 연결된 속성을 포함해야 한다.

  • db.ForeignKey --> 기준 모델과 연결할 수 있다.

    *데이터베이스에서는 기존 모델과 연결된 속성을 외부 키(foreign key)라고 한다.

  • 'question.id'--> question 테이블의 id 컬럼을 의미한다.

    *Question 모델을 통해 테이블이 생성되면 테이블명은 question이 된다.

  • ondelete='CASCADE'--> 데이터베이스에서 쿼리를 이용하여 질문을 삭제하면 해당 질문에 달린 답변도 함께 삭제된다.
    *ondelete에 지정한 값은 삭제 연동 설정이다. 즉 답변 모델의 question_id 속성은 질문 모델의 id 속성과 연결된다.

*question

question = db.relationship('Question', backref=db.backref('answer_set'))
  • db.relationship --> 답변 모델에서 질문 모델을 참조하기 위해서 db.Colum 이아닌 db.relationship을 사용함
  • 'Question'--> 참조할 모델명
  • backref --> 역참조 설정.
    *역참조란 쉽게 말해 질문에서 답변을 거꾸로 참조하는 것을 의미한다. 한 질문에는 여러개의 답변이 달릴 수 있는데 역참조는 이 질문에 달린 답변을 참조할 수 있게 한다.

모델을 이용해 테이블 자동으로 생성하기

모델을 구상하고 생성했으므로 플라스크의 Migrate 기능을 이용해 데이터베이스 테이블을 생성할 수 있다.

1) 모델 가져오기

앞서 생성한 모델들을 플라스크의 Migrate 기능이 인식할 수 있도록 Pybo/__init__.py 파일을 수정한다.

(... 생략 ...)

  # ORM
  db.init_app(app)
  migrate.init_app(app, db)
  from . import models

(... 생략 ...)

2) 데이터베이스 변경을 위한 리비전 파일 생성하기

명령 프롬프트에서 flask db migrate 명령을 수행한다.

(myproject) c:\projects\myproject> flask db migrate
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table ‘question’
INFO [alembic.autogenerate.compare] Detected added table ‘answer’
Generating c:\projects\myproject\migrations\versions\18634a293520_.py ... done

이 명령을 수행하면 18634a293520_.py처럼 데이터베이스 변경 작업을 위한 리비전 파일이 생성된다.

*리비전(revision)이란 생성된 18634a293520.py 파일명에서 18634a293520을 가리킨다. 리비전은 flask db migrate 명령을 수행할 때 무작위로 만들어진다.

3) 데이터베이스 갱신하기

이어서 flask db upgrade 명령으로 리비전 파일을 실행한다.

(myproject) c:\projects\myproject> flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 18634a293520, empty message

이 과정에서 데이터베이스에 모델 이름과 똑같은 question과 answer라는 이름의 테이블이 생성된다. 지금까지 설명한 1~3단계를 제대로 수행했다면 projects/myproject 디렉터리에 pybo.db 파일이 생성되었을 것이다. pybo.db가 바로 SQLite 데이터베이스의 데이터 파일이다.

생성된 테이블 살펴보기

1) DB Browser for SQLite 설치하기

https://sqlitebrowser.org/dl 에 접속하여 DB Browser for SQLite 을 설치한다.
*자신의 운영체제에 맞는 설치파일을 내려받고, 바로가기를 생성하도록 한다.

2) DB 브라우저에서 pybo.db 열기

방금 설치한 DB 브라우저를 실행하고 메뉴에서 [파일 → 데이터베이스 열기]를 선택한다. 그리고 앞선 실습에서 생성한 C:/projects/myproject/pybo.db 데이터베이스 파일을 선택하고 <열기>를 누른다.

모델 사용하기

1) 플라스크 셸 실행하기

명령 프롬프트에서 flask shell 명령으로 실행한다.

(myproject) c:\projects\myproject>flask shell
Python 3.8.5 (tags/v3.8.2:7b3ab59, Feb 25 2020, 22:45:29) [MSC v.1916 32 bit (Intel)] on win32
App: pybo [development]
Instance: C:\projects\myproject\instance
>>> 

2) 질문 데이터 저장하기

다음 명령을 수행해 Question과 Answer 모델을 플라스크 셸에 불러와 Question 모델 객체를 생성한다.

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

Question 모델의 create_date 속성은 DateTime 유형이므로 datetime.now 함수로 현재 일시를 대입했다.


데이터베이스에 저장하려면 다음처럼 SQLAlchemy의 db 객체를 사용해야 한다.

>>> from pybo import db
>>> db.session.add(q)
>>> db.session.commit()

신규 데이터를 저장할 때는 add 함수를 사용한 다음 commit 함수까지 실행해야 한다.
db.session은 데이터베이스와 연결된 세션, 즉 접속된 상태를 의미한다. 데이터베이스를 처리하려면 이 세션이 필요하다. 그리고 세션을 통해서 데이터를 저장, 수정, 삭제 작업을 한 다음에는 반드시 db.session.commit()으로 커밋을 해주어야 한다.

*커밋은 취소할 수 없다. 수행한 작업을 취소하기 위해선 커밋
이전(세션 작업 도중)에 진행해야 한다. db.session.rollback() 으로 되돌리기 실행

q.id 를 통해 데이터가 잘 생성되었는지 확인할 수 있다.

>>> q.id
1

*id는 Question 모델의 기본 키다. id는 앞에서 모델을 생성할 때 설정했던대로 데이터를 생성할 때 속성값이 자동으로 1씩 증가한다.

>>> q = Question(subject='플라스크 모델 질문입니다.', content='id는 자동으로 생성되나요?', create_date=datetime.now())
>>> db.session.add(q)
>>> db.session.commit()
>>> q.id
2

두 번째 질문 데이터를 생성했을때 두번째 질문 데이터의 id가 2로 출력되는 것을 확인할 수 있다.

3) 데이터 조회하기

>>> Question.query.all()
[<Question 1>, <Question 2>]
  • Question.query.all() --> 데이터베이스에 저장된 질문 데이터 전부를 조회한다. 이 함수는 Question 객체 리스트를 반환한다. 결과에서 보이는 숫자 1과 2는 Question 객체의 id 속성값이다.

>>> Question.query.filter(Question.id==1).all()
[<Question 1>]
  • filter --> 조건에 맞는 데이터를 모두 반환해 준다. 여기서는 기본 키인 id를 이용했으므로 값을 1개만 반환한다.


    *id는 유일한 값이므로 filter 함수 대신 get 함수를 이용해 조회할 수도 있다.
>>> Question.query.get(1)
<Question 1>

다만 get 함수로 조회하면 리스트가 아닌 Question 객체 1개만 반환된다.

>>> Question.query.filter(Question.subject.like('%플라스크%')).all()
[<Question 2>]
  • filter와 like --> 조건에 맞는 문자열이 포함된 Question 모델이 반환된다.
    플라스크%: "플라스크"로 시작하는 문자열
    %플라스크: "플라스크"로 끝나는 문자열
    %플라스크%: "플라스크"를 포함하는 문자열

    *대소 문자 구분하지 않고 문자열을 찾으려면 like 함수 대신 ilike 함수를 사용한다.

4) 데이터 수정하기

>>> q = Question.query.get(2)
>>> q
<Question 2>
>>> q.subject = 'Flask Model Question'
>>> db.session.commit()

데이터를 수정할 때는 단순히 대입 연산자를 사용하면 된다.

5) 데이터 삭제하기

>>> q = Question.query.get(1)
>>> db.session.delete(q)
>>> db.session.commit()

첫 번째 데이터를 조하한 후 delete함수를 이용해 삭제했다.

>>> Question.query.all()
[<Question 2>]

첫 번째 질문이 삭제되어 두번째 질문만 조회 된걸 알 수 있다.

6) 답변 데이터 생성 후 저장하기

>>> from datetime import datetime
>>> from pybo.models import Question, Answer
>>> from pybo import db
>>> q = Question.query.get(2)
>>> a = Answer(question=q, content='네 자동으로 생성됩니다.', create_date=datetime.now())
>>> db.session.add(a)
>>> db.session.commit()

답변 데이터를 생성하기 위해 질문 데이터를 구한다.
id가 2인 질문 데이터를 가져온 다음 qdp 저장한다.
그런 다음 Answer 모델의 question 속성에 방금 가져온 q를 대입해 답변데이터를 생성한다.

Answer도 Question 모델과 마찬가지로 id 속성이 기본 키이므로 값이 자동으로 생성된다. 다음 명령으로 id값을 확인해 보고 이 값을 이용해 데이터도 확인해 보자.

>>> a.id
1
>>> a = Answer.query.get(1)
>>> a
<Answer 1>

7) 답변에 연결된 질문 찾기 vs 질문에 달린 답변 찾기

앞에서 구성한 Answer 모델의 question 속성을 이용하면 "답변에 연결된 질문"을 조회할 수 있다.

>>> a.question
<Question 2>

Question 모델과 Answer 모델은 현재 연결된 상태이고, Answer 모델의 question 속성에 역참조 설정 backref=db.backref('answer_set')을 적용했다. 그러므로 이를 사용하면 질문과 연결된 답변을 쉽게 가져올 수 있다.

>>> q.answer_set
[<Answer 1>]

좋은 웹페이지 즐겨찾기