Flask 프레임워크 기반 데이터베이스 및 템플릿 개발 가이드

데이터베이스 마이그레이션 설정

SQLAlchemy와 Alembic을 활용한 데이터베이스 스키마 관리는 다음과 같은 세 단계로 진행됩니다.

flask db init
flask db migrate
flask db upgrade
  • db init: 마이그레이션 파일을 저장할 migrations 폴더를 생성합니다.
  • db migrate: ORM 모델 변화를 기반으로 자동으로 변경 스크립트를 생성합니다. 예: 850b7054a4f6_.py.
  • db upgrade: 생성된 스크립트를 실행하여 실제 데이터베이스에 스키마를 반영하고 버전 기록을 업데이트합니다.

Jinja2 템플릿 기능 활용

템플릿 내에서 동적 렌더링과 재사용 가능한 구조를 구성하는 방법은 다음과 같습니다.

반복 및 조건 처리

<select name="floorId" id="floorId">
    {% for floor in floors %}
        <option value="{{ floor.id }}"
                {% if floor.id == room.floor_id %}selected{% endif %}>
            {{ floor.name }}
        </option>
    {% endfor %}
</select>
  • loop.index: 현재 반복 인덱스 (1부터 시작)
  • loop.first, loop.last: 첫 번째 또는 마지막 항목 여부
  • loop.length: 전체 항목 수

템플릿 포함 및 재사용

{% include 'system/common/header.html' %}

{% include 'system/common/footer.html' %}

폼 데이터 복원

@bp.get('/edit/<int:id>')
def edit(id):
    repair = curd.get_by_id(Repair, id)
    rooms = Room.query.all()
    return render_template('system/repair/edit.html', repair=repair, rooms=rooms)
<input type="text" value="{{ repair.id }}" name="id" class="layui-input">
<textarea name="remark" lay-verify="required" class="layui-textarea">
{{ repair.remark }}
</textarea>

ORM 및 데이터 시리얼라이제이션

SQLAlchemy의 주요 기능

  • 객체 관계 매핑: Python 클래스를 데이터베이스 테이블과 연결하여 쿼리 없이 객체 메서드로 조작 가능.
  • 다중 데이터베이스 호환성: MySQL, PostgreSQL, SQLite 등 다양한 백엔드 지원.
  • 고급 쿼리 언어: SQL 표현식 언어로 복잡한 조건, 조인, 집계 등을 자유롭게 작성.
  • 세션 기반 트랜잭션 관리: db.session.commit()으로 일괄 적용.
  • 마이그레이션 통합: Alembic과 연동하여 스키마 변경 이력 관리.

SQLAlchemyAutoSchema의 역할

SQLAlchemyAutoSchemaMarshmallow 확장 라이브러리로, 데이터 모델과 JSON 간 자동 변환을 제공합니다.

  • 모델 필드 자동 매핑 → String, Integer 등 필드 생성
  • dump()로 모델 → 딕셔너리 → JSON 변환
  • load()로 요청 데이터 → 유효성 검사 후 모델 인스턴스 생성
  • 관계 필드(예: 다대일)도 자동으로 중첩 시리얼라이즈
  • 커스터마이징 가능: only, exclude, required 등 옵션 지정

CRUD 작업 실습

삽입

order = RoomOrder(user_id=userId, user_name=username, order_time=orderTime, remark=remark)
db.session.add(order)
db.session.commit()

수정

# 단일 수정
room = Room.query.get(roomId)
room.curr_num -= 1
db.session.commit()

# 다중 수정
RoomOrder.query.filter_by(id=id).update({'state': 0})
db.session.commit()

삭제

# 단일 삭제
db.session.delete(Room.query.get(id))
db.session.commit()

# 다중 삭제
User.query.filter(User.id.in_([1,2,3])).delete()
db.session.commit()

조회

# 전체 조회
RoomOrder.query.all()

# 조건 조회
RoomOrder.query.filter_by(name='1교').first()

# 복합 조건 + 정렬 + 페이지네이션
filters = []
if faculty_id:
    filters.append(Score.faculty_id == faculty_id)
if name:
    filters.append(Score.name.contains(name))

Score.query.filter(*filters).order_by(Score.id.desc()).layui_paginate()

시리얼라이제이션 직접 구현

Marshmallow 사용 전에는 모델에 to_dict() 메서드를 직접 추가해야 합니다.

class AdminLog(db.Model):
    __tablename__ = 'admin_log'
    id = db.Column(db.Integer, primary_key=True)
    method = db.Column(db.String(10))
    uid = db.Column(db.Integer)
    url = db.Column(db.String(255))
    desc = db.Column(db.Text)
    ip = db.Column(db.String(255))
    success = db.Column(db.Integer)
    user_agent = db.Column(db.Text)
    create_time = db.Column(db.DateTime, default=datetime.datetime.now)

    def to_dict(self):
        return {
            "id": self.id,
            "method": self.method,
            "uid": self.uid,
            "url": self.url,
            "desc": self.desc,
            "ip": self.ip,
            "success": self.success,
            "user_agent": self.user_agent
        }
log = AdminLog(...)
db.session.add(log)
db.session.commit()
print(json.dumps(log.to_dict(), indent=4))

복합 조건 쿼리 작성 팁

  • filter() 체이닝: .filter(User.name == 'A').filter(User.age > 25)
  • and_() 사용: filter(and_(User.name == 'A', User.age > 25))
  • or_() 사용: filter(or_(User.name == 'A', User.age < 18))
  • filter_by()는 등가 비교에만 사용: filter_by(name='A', age=25)

태그: flask sqlalchemy Jinja2 ORM marshmallow

6월 1일 21:19에 게시됨