Flask에서 SQLite 3 사용하기

여러분은 Flask에서 필요할 때 데이타베이스 연결을 열고 문맥이 끝났을 때 (보통 요청이 끝에서) 연결을 닫는 것을 쉽게 구현할 수 있다:

import sqlite3
from flask import _app_ctx_stack

DATABASE = '/path/to/database.db'

def get_db():
    top = _app_ctx_stack.top
    if not hasattr(top, 'sqlite_db'):
        top.sqlite_db = sqlite3.connect(DATABASE)
    return top.sqlite_db

@app.teardown_appcontext
def close_connection(exception):
    top = _app_ctx_stack.top
    if hasattr(top, 'sqlite_db'):
        top.sqlite_db.close()

데이타베이스를 지금 사용하기 위해서 어플리케이션에서 필요한 전부는 활성화된 어플리케이션 문맥을 갖거나 (전달중인 요청이 있다면 항상 활성화 된 것이다) 어플리케이션 문맥 자체를 생성하는 것이다. 그 시점에 get_db 함수는 현재 데이타베이스 연결을 얻기위해 사용될 수 있다. 그 문맥이 소멸될 때마다 그 데이타베이스 연결은 종료될 것이다.

예제:

@app.route('/')
def index():
    cur = get_db().cursor()
    ...

주석

before-request 핸들러가 실패하거나 절대 실행되지 않더라도, teardown 요청과 appcontext 함수는 항상 실행되다는 것을 명심하길 바란다. 그런 이유로 우리가 데이타베이스를 닫기전에 거기에 데이타베이스가 있었다는 것을 여기서 보장해야한다.

필요할 때 연결하기

이 접근법의 장점은 (첫 사용시 연결하는 것) 정말 필요할 때만 연결이 열린다는 것이다. 여러분이 요청 문맥 밖에서 이 코드를 사용하고 싶다면 파이썬 쉘에서 수동으로 어플리케이션 문맥을 열고서 그 연결을 사용할 수 있다:

with app.app_context():
    # now you can use get_db()

쉬운 질의하기

이제 각 요청 핸들링 함수에서 현재 열린 데이타베이스 연결을 얻기 위해 여러분은 g.db 에 접근할 수 있다. SQLite 로 작업을 간단하게 하기 위해, 행(row) 팩토리 함수가 유용하다. 결과를 변환하기 위해 데이타베이스에서 반환된 모든 결과에 대해 실행된다. 예를 들면 튜플 대신 딕셔너리를 얻기 위해 아래와 같이 사용될 수 있다:

def make_dicts(cursor, row):
    return dict((cur.description[idx][0], value)
                for idx, value in enumerate(row))

db.row_factory = make_dicts

덧붙이자면 커서를 얻고, 결과를 실행하고 꺼내는 것을 결합한 질의 함수를 제공하는 것은 괜찮은 생각이다:

def query_db(query, args=(), one=False):
    cur = get_db().execute(query, args)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv

이 유용한 작은 함수는 행 팩토리와 결합되어 데이타베이스와 작업을 단지 원형의 커서와 연결 객체를 사용하는 것 보다 훨씬 더 기분 좋게 만든다.

아래에 그것을 사용하는 방법이 있다:

for user in query_db('select * from users'):
    print user['username'], 'has the id', user['user_id']

또는 여러분이 단지 단일 결과를 원한다면:

user = query_db('select * from users where username = ?',
                [the_username], one=True)
if user is None:
    print 'No such user'
else:
    print the_username, 'has the id', user['user_id']

변수의 일부분을 SQL 구문으로 전달하기 위해, 구문 안에 물음표를 사용하고 목록으로 인자안에 전달한다. 절대로 직접 인자들을 문자열 형태로 SQL 구문에 추가하면 안되는데 왜냐하면 SQL 인젝션(Injections) 을 사용해서 그 어플리케이션을 공격할 수 있기 때문이다.

초기 스키마

관계형 데이타베이스들은 스키마를 필요로 하기 때문에, 어플리케이션들은 데이타베이스를 생성하는 schema.sql 파일을 종종 만들어낸다. 그 스키마에 기반한 데이타베이스를 생성하는 함수를 제공하는 것은 괜찮은 생각이다. 아래 함수는 여러분을 위해 그러한 작업을 할 수 있다:

def init_db():
    with app.app_context():
        db = get_db()
        with app.open_resource('schema.sql') as f:
            db.cursor().executescript(f.read())
        db.commit()

그리고 나면 여러분은 파이썬 쉘에서 그런 데이타베이스를 생성할 수 있다:

>>> from yourapplication import init_db
>>> init_db()