데이터베이스 마이그레이션 설정
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의 역할
SQLAlchemyAutoSchema는 Marshmallow 확장 라이브러리로, 데이터 모델과 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)