Notice
Recent Posts
Recent Comments
05-21 07:17
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Byeol Lo

Flask - 4. Define and Access the Database 본문

BackEnd/Flask

Flask - 4. Define and Access the Database

알 수 없는 사용자 2024. 2. 12. 15:11

 flask 어플리케이션은 SQLite 데이터베이스를 사용하고, user의 정보나 posts들을 저장할 수 있도록 할 수 있다. 기본적으로 python에선 sqlite3 모듈에 SQLite 데이터베이스에 대한 유용한 함수들이 내장되어 있기 때문에 그것을 사용하면 된다. 또한 SQLite의 장점으로는 편리하다는 것이다. database 서버를 따로 분리시키지 않고, 세팅도 따로 해줄 필요가 없기 때문이다. 하지만 동시적인 request를 받았을때 write되는 과정에서 sequential하게 쓰기 때문에 느리다는 것이다. 하지만 이는 사용자가 많을 때의 얘기이며, 자기가 구현하고자 하는 상황에 맞게 구현시키면 된다. 하지만 유저가 많을 경우에는 다른 데이터베이스 서버를 사용하여 구분하는 것이 좋다.

 

Connect to the Database

  밑은 db를 연동시키는 코드이다.

# flaskr/db.py

from flask import current_app, g

import sqlite3
import click

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlites3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)
    
    if db is not None:
        db.close()

 우선 Flask에는 special object가 있다는 것을 알 것이다. 가령 g라던가, current_app이라던가 등등이 있다. g는 전역객체 임을 알 것이고, 여기에 자주 사용되어지는 데이터들을 저장하여서 다중 함수들에 대한 중복 액세스를 피할 수 있을 것이다. 또한 get_db 가 동일한 요청으로 두번 호출되는 경우에도 새 연결을 생성하는 대신에 기존에 있던 connection을 재사용하고 이 연결을 저장해놓는 용도이다. 그래서 if 문에서는 g에 db라는 것이 있는지 를 살피고 있다면 db에 대한 연결을 무시, 아니라면 새 연결을 생성하게 되는 것이다.

 그리고 sqlite3.connect에서 생성했던 DATABASE의 value(file path)를 가져와 해당 path에 있는 file로 연동해준다. 이때 path는 current_app에서 가져올 수 있는데 current_app은 현재 활성화된 Flask 애플리케이션을 나타내는 객체이다. 여기서 해당 application의 configuration을 설정했던 그 값을 가져오는 것 뿐이다. DATABASE의 파일 경로에 파일은 아직 존재할 필요가 없고, 데이터베이스를 초기화하는 시점에 존재하게 된다.

 그리고 db의 열에 접근하는 방식을 db.row_factory로 설정할 수 있는데, sqlite3.Row로 한다면 column의 이름을 통해서 행이 사전 형태로 반환되도록 연결한다는 것이다.

 

Create the Table

 SQLite에서는 data가 table들과 column들에 저장되어 지는데, 우리의 게시판을 만들기 위해서 RDBMS의 질의를 위한 스키마를 정의해야한다.

# flaskr/schema.sql
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT UNIQUE NOT NULL,
    password TEXT NOT NULL
);

CREATE TABLE post (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    author_id INTEGER NOT NULL,
    created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    title TEXT NOT NULL,
    body TEXT NOT NULL,
    FOREIGN KEY (author_id) REFERENCES user (id)
);

다음과 같이 table 을 만들어준다. 그리고 이를 실행하는 python 함수를 만들어줄건데,

# flask/db.py
def init_db():
    db = get_db()
    
    with current_app.open_resource('schema.sql') as f:
        db.executesscript(f.read().decode('utf8'))


@click.command('init-db')
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

 위와 같이 init_db의 기능만 하는 함수를 따로, 그리고 실제로 view의 역할을 해서 해당 함수를 호출하는 애를 따로 만들어주어서 기능 분리를 시켜준다. view 측면에서 request를 받은 함수로는 click.echo로 CLI 창에 메시지로 피드백을 준다.

 

Register with the Application

 이제 close_db, init_db_command를 사용할 수 있도록 register을 했다면 db를 이용할 수 있도록 한다.

# flaskr/db.py
def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

teardown_appcontext는 응답을 반환한 후에 정리할 때 해당 함수를 호출하도록 Flask에 지시한다. 그리고 cli.add_command는 플라스크 명령으로 호출할 수 있는 새 명령을 추가하는 것이다. 즉, teardown_appcontext는어플리케이션 컨텍스트가 종료가 될 때 호출되는 함수를 등록한 것인데, 예를 들어서 어플리케이션의 컨텍스트가 닫혔다면 코드가 더이상 실행되지 않는 것이기 때문에 종료가 된 것인데, 데이터 베이스 연결을 닫거나, 임시 파일을 삭제하는 등의 작업을 수행하는 것이다. 그리고 두 번째 코드는 사용자 정의 cli 명령어를 등록한 것 뿐인데, cli.add_command를 통해 나중에 터미널에서 flask [명령어] 형태로 호출을 할 수 있다는 것이다. 이때 명령어는 annotation의 init-db가 될 것이다.

# flaskr/__init__.py

def create_app():
    
    # ...Omitted
    
    from . import db
    db.init_app(app)
    
    return app

 

Initialize the Database File

 이제 init-db가 명령어로 등록되었기 때문에 flask cli에서 해당 명령을 호출하여 db를 초기화 가능하다.

flask --app flaskr init-db

하지만 다음과 같은 에러가 뜰 수도 있는데

sqlite3.Warning: You can only execute one statement at a time.

이때는 init_db의 sql command를 실행시키는데 하나의 명령씩만 실행시킬 수 있지만, 한꺼번에 실행시키려고 할 때 뜨는 오류이다. 따라서 다음과 같이 수정해준다.

def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        sql_commands = f.read().decode('utf8').split(";")

        for _ in sql_commands:
            db.execute(_)

이제 위 커맨드를 다시 실행해주면 flaskr.sqlite에 파일이 생성되고 db를 이용할 수 있게 된다.

'BackEnd > Flask' 카테고리의 다른 글

Flask - 6. Tutorial Templates  (0) 2024.02.14
Flask - 5. Tutorial Blueprint and View  (1) 2024.02.14
Flask - 3. Tutorial Application Setup  (0) 2024.02.12
Flask - 2. Tutorial Project Layout  (0) 2024.02.08
Flask - 1. Quick start  (1) 2024.02.08
Comments