ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flask Project - MyBlog Flask-sqlalchemy(with Flask-migrate) DB 관리
    프로젝트 2024. 4. 8. 21:13
    반응형

    DB 관리 통합

    ORM: Flask-SQLAlchemy

    Flask는 데이터베이스 기능이 내장되어 있지 않습니다. 따라서 적절한 데이터베이스를 선택하여 사용해야 하고, 이렇게 될 경우 프로젝트가 특정 데이터베이스에 종속될 수 있습니다.

    이 때 ORM은 데이터 객체 모델과 데이터베이스를 매핑해주기 때문에, 특정 데이터베이스에 종속되지 않고 일관된 OOP 코드를 작성할 수 있게 됩니다.

     

    파이썬 환경에서 사용할 수 있는 대표적인 ORM은 sqlalchemy가 있습니다. 그리고 Flask app에서 더 쉽게 사용가능하도록 만든 flask-sqlalchemy가 존재합니다. 따라서 저는 flask-sqlalchemy를 선택했습니다.

    데이터베이스 마이그레이션: Flask-migrate

    데이터베이스 마이그레이션이란 데이터베이스 스키마의 변경 사항을 적용하는 프로세스를 의미합니다. python에서는 alembic을 사용해 데이터베이스를 관리합니다. 이를 flask에서 사용하기 유용한 버전으로 custom한 것이 flask-migrate입니다. 그리고 flask-migrate는 아래와 같은 기능들을 간편하게 활용할 수 있습니다.

    1. 스키마 변경 관리: 스키마 변경 사항을 추적하고 버전 관리를 제공하여 스키마를 쉽게 관리할 수 있도록 도와줍니다.
    2. 자동 마이그레이션 생성: 데이터베이스 스키마 변경 사항을 기반으로 마이그레이션 스크립트를 자동으로 생성합니다. 이러한 스크립트는 데이터베이스에 변경 사항을 적용하는 데 사용됩니다.
    3. 테스트 및 복구: 데이터베이스 마이그레이션을 테스트하고 롤백하는 것이 간편해집니다. 이를 통해 개발자는 변경 사항을 적용하기 전에 데이터베이스의 이전 상태로 복원할 수 있습니다.

    기초 환경 셋팅하기

    가장 먼저 가상환경 활성화해주시고, pip install flask-slqalchemy flask-migrate flask-login 해주세요.

    1. 관련 환경 변수 제어(config.py)

    # blog/config.py
    import os
    class Config():
        '''
        이전 생략
        '''
        BASE_DIR = os.path.dirname(__file__)
        BASE_DB_NAME = '원하는 db 이름'
    
        SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, BASE_DB_NAME))
        SQLALCHEMY_TRACK_MODIFICATIONS = False # 수정사항 추적 X = 메모리 절약
        WTF_CSRF_ENABLED = True # Cross Site Request Forgery(CSRF) 공격 방지

    SQLALCHEMY_DATABASE_URI는 선택한 DB에 따라 다른 형식으로 선언할 수 있습니다.

    어떤 파라미터들이 있는지 아래 링크를 참조해주세요.

    https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/

     

    Configuration — Flask-SQLAlchemy Documentation (2.x)

    Configuration The following configuration values exist for Flask-SQLAlchemy. Flask-SQLAlchemy loads these values from your main Flask config which can be populated in various ways. Note that some of those cannot be modified after the engine was created so

    flask-sqlalchemy.palletsprojects.com

    2. 모델 생성 + 플라스크 app과 연결(bind)

    flask-sqlalchemy를 사용하기 위해 해당 모듈의 Model Class, flask-login을 사용하기 위해 UserMixin Class를 상속받도록 했습니다.

     

    id는 primary_key로, 새로운 객체 생성시 자동으로 할당됩니다.

    date_created는 서버에서 관리하도록 default값을 지정했습니다.

    비밀번호 암호화 + default값 할당을 위해 생성자를 따로 생성해주었습니다.

     

    비밀번호 필드에서 사용한 generate_password_hash 메소드는 werkzeug.security 모듈에서 제공하는 메소드입니다. werkzeug는 플라스크 의존 라이브러리 중 하나로, 주어진 문자열을 안전한 해시 값으로 변환할 수 있습니다. 짝을 이루는 check_password_hash 메소드와 함께 사용합니다.

    # blog/models.py
    from flask_sqlalchemy import SQLAlchemy
    from flask_login import UserMixin
    from flask_migrate import Migrate
    from datetime import datetime, timedelta, timezone
    from werkzeug.security import generate_password_hash
    
    # 한국 시간대 오프셋(UTC+9)을 생성합니다.
    KST_offset = timezone(timedelta(hours=9))
    
    db = SQLAlchemy()
    migrate = Migrate()
    
    # flask-login 사용하기 위해 UserMixin 상속
    class User(db.Model, UserMixin):
        __tablename__ = 'user'                                                      # 테이블 이름 명시적 선언
        id = db.Column(db.Integer, primary_key=True)                                # primary key 설정
        username = db.Column(db.String(150), unique=True)                           # username unique
        email = db.Column(db.String(150), unique=True)                              # email unique
        password = db.Column(db.String(150))                                        # password 
        date_created = db.Column(db.DateTime, default=datetime.now(KST_offset))     # 회원가입 날짜, 시간 기록
        post_create_permission = db.Column(db.Boolean, default=False)               # 글 작성 권한 여부
        admin_check = db.Column(db.Boolean, default=False)                          # 관리자 권한 여부
        
        def __init__(self, username, email, password, post_create_permission=False, admin_check=False):
            self.username = username
            self.email = email
            self.password = generate_password_hash(password)
            self.date_created = datetime.now(KST_offset)
            self.post_create_permission = post_create_permission 
            self.admin_check = admin_check
    
        def __repr__(self):
            return f'User {self.id}: {self.username}'  
            
    # blog/__init__.py
    from flask import Flask
    from flask_login import LoginManager
    from .models import db, migrate
    
    def create_app(config):
        app = Flask(__name__)
        app.config.from_object(config) # 환경변수 설정 코드
        db.init_app(app)
        migrate.init_app(app, db)
        
        # login_manager 설정 코드
        login_manager = LoginManager()
        login_manager.init_app(app) # app 연결

    3. 데이터베이스 생성

    테이블 생성 시, 기존에 테이블이 존재하지 않는 경우에만 생성이 되므로, 테이블 존재 여부에 대해 판단하지 않아도 됩니다.

    from blog import create_app
    from blog.config import Config
    from blog.models import db
    
    app = create_app(config=Config)
    with app.app_context():
        # db.Model 상속한 모든 클래스 추적해서 테이블 생성
        db.create_all()
    app.run(debug=True, port=Config.PORT)

    여기까지 DB 통합 관리를 위한 기초 작업입니다.

     

    추후 모델 스키마가 변경되거나 삭제되는 경우를 대비해서 flask-migrate를 사용하는 방법에 대해 소개하겠습니다.

     

    flask-migrate를 설치한 뒤 프로젝트의 루트 폴더에서 터미널을 엽니다.

     

    1. flask db init: 명령어를 입력하면 migrations 폴더가 생성되면서 db 스키마를 추적하게 됩니다.

    2. versions 폴더: 아래 명령어 2개 모두 migrations/versions 폴더 내에 revision 파일을 생성합니다. 해당 파일을 통해 db upgrade, downgrade를 손쉽게 진행할 수 있습니다.

    2-1. flask db migrate: 스키마 변경 사항 자동으로 추적 후 스크립트 파일(revision)생성

    2-2. flask db revision: revision  파일 직접 생성 후 수동으로 스크립트 파일 작성
    3. flask db [upgrade/downgrade]: 가장 최근 버전으로 upgrade or downgrade 가능

    반응형
Designed by Tistory.