SQLAlchemy는 SQL(Structured Query Language) 데이터베이스 액세스 툴킷 및 ORM(Object-Relational Mapping)
라이브러리입니다. 데이터베이스 테이블과 파이썬 클래스 간에 매핑을 제공합니다. 이를 통해 개발자는 SQL 쿼리 대신 파이썬 객체를 사용하여 데이터를 다룰 수 있습니다.
Flask-SQLAlchemy는 SQLAlchemy의 기능을 Flask와 통합하여 데이터베이스와의 상호작용을 간편하게 만들어줍니다.
💡 본 글은 예제 코드를 이용하여 설명합니다.
본 예제는 데이터베이스를 MariaDB로 사용하였습니다.
flask/source/config.py를 살펴보면 데이터베이스에 접속하기 위해 각정 정보를 조합하여 링크를 생성합니다.
SQLALCHEMY_TRACK_MODIFICATIONS
는 변경을 자동으로 추적하고, 변경 사항을 데이터베이스에 반영하는 옵션으로 False를 적용하면 commit을 수동으로 호출해야 하는 단점을 가지는 반면 속도는 향상될 수 있습니다.
class Config:
# 코드 생략
db_pt = os.environ["MARIADB_PORT"]
db_id = os.environ["MARIADB_USER"]
db_pw = os.environ["MARIADB_PASSWORD"]
db_nm = os.environ["MARIADB_DATABASE"]
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{db_id}:{db_pw}@db:{db_pt}/{db_nm}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
데이터베이스 정보를 통해 연결하기 위해서 flask/source/my_app/__init__.py에 코드를 작성하였습니다.
데이터베이스 객체를 생성하고 데이터베이스에 접속하여 정의된 클래스 객체들을 이용하여 테이블을 생성합니다.
db = SQLAlchemy()
# 코드 생략
def create_app():
app.config.from_object(obj=config["development"])
with app.app_context():
# 코드 생략
db.init_app(app=app)
db.create_all()
flask/source/my_app/models/user.py를 살펴보면 데이터베이스의 테이블로 생성될 사용자 클래스를 정의하였습니다.
__tablename__
은 데이터베이스의 테이블 이름으로 정의되며 각 클래스 변수들은 테이블의 열로 정의됩니다.
테이블의 열은 Column을 이용하여 정의할 수 있습니다.
relationship을 테이블의 열로 정의되지는 않지만, 밀접한 클래스 간 데이터를 참조하기 쉽게 하기 위하여 JOIN과 같은 역할을 합니다.
class User(db.Model, UserMixin):
__tablename__ = "users"
id = Column(
INTEGER(display_width=11, unsigned=True),
primary_key=True,
autoincrement="auto",
comment="사용자 식별 값"
)
username = Column(
VARCHAR(length=50),
unique=True,
nullable=False,
comment="사용자 아이디"
)
password = Column(
VARCHAR(length=255),
nullable=False,
comment="사용자 비밀번호"
)
roles = relationship(
argument="Role",
secondary="rel_user_roles",
backref=backref(name="users", lazy=True),
uselist=True
)
comments = relationship(
argument="Comment",
secondary="rel_user_comments",
backref=backref(name="users"),
uselist=True
)
def __init__(self, username:str, password:str, roles:list):
self.username = username
self.password = generate_password_hash(password=password)
self.roles = roles
# 코드 생략
사용자마다 역할을 다수 가질 수 있기 때문에 uselist가 True로 설정되게 하였으며, Role이라는 역할 테이블과 rel_user_roles라는 중간 테이블을 이용하여 User 객체가 가지는 역할을 쉽게 접근할 수 있습니다.
user_roles = [role.name for role in current_user.roles]
flask/source/my_app/views/index.py는 사용자 객체를 생성하고 데이터베이스에 적용하는 예제입니다.
모델을 정의 시 필요한 필드들을 클래스 객체를 통해 추가합니다. id 는 autoincrement이기 때문에 사용자가 정의할 필요가 없습니다.
user = User(
username=form.username.data,
password=form.password.data,
roles=[role]
)
이후, SQLAlchemy 객체를 이용하여 데이터베이스에 적용하기 위해 commit
을 사용합니다.
db.session.add(instance=user)
try:
db.session.commit()
위 코드를 통해 데이터베이스에 적용된 데이터는 아래와 같은 형식이 됩니다.
id | username | password |
---|---|---|
2 | user | ~~~ |
id | user_id | role_id |
---|---|---|
2 | 2 | 2 |
flask/source/my_app/views/index.py는 데이터베이스에서 사용자를 조회하는 코드입니다.
객체의 query
를 이용하여 데이터베이스 질의를 하며, filter_by
를 이용하여 조건을 붙입니다.
검색된 결과 중 첫번째 데이터를 뽑기 위해서는 first()
를 호출하며, 모든 데이터를 뽑기 위해서는 all()
을 호출합니다.
# 코드 생략
user = User.query.filter_by(username=form.username.data).first()
filter_by
가 아닌 filter
를 사용하여도 됩니다. 사용 방법은 아래와 같이 filter_by와는 차이가 있음을 유의합니다.
user = User.query.filter(User.username==form.username.data).first()
flask/source/my_app/views/board.py는 게시글을 수정하는 함수입니다.
post라는 모델의 필드 중 title과 content라는 필드에 폼으로부터 전달된 데이터를 저장합니다.
post.title = form.title.data
post.content = form.content.data
이후, 데이터베이스에 적용하기 위해 commit
을 사용합니다.
db.session.commit()
flask/source/my_app/views/board.py는 게시글을 삭제하는 함수입니다.
post라는 모델을 delete
함수에 전달 후 commit
하여 적용하면 데이터베이스에서 해당 데이터가 삭제됩니다.
db.session.delete(instance=post)
db.session.commit()