HOWTO · Flask
Flask SQLAlchemy에서 레코드 삭제
Flask-SQLAlchemy에서 레코드를 삭제하는 실용적인 방법을 알아봅니다: db.session.get()으로 기본 키의 모델 인스턴스를 불러오고, db.session.delete()로 삭제 대상으로 표시한 뒤, db.session.commit()으로 삭제를 저장합니다.
이 페이지의 내용
Flask-SQLAlchemy에서 레코드를 삭제하는 작업은 세 단계로 이루어집니다. 모델 인스턴스를 불러오고, db.session.delete()에 전달한 다음, db.session.commit()로 트랜잭션을 확정합니다. 핵심 패턴은 다음과 같습니다:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
def delete_customer(customer_id):
customer = db.session.get(Customer, customer_id)
if customer is not None:
db.session.delete(customer)
db.session.commit()
db.session.get()는 기본 키로 행을 조회하며 일치하는 행이 없으면 None을 반환합니다. db.session.delete()는 불러온 인스턴스를 세션 안에서 삭제 대상으로 표시하고, db.session.commit()는 실제 DELETE 문을 실행합니다. 커밋이 성공하기 전까지 행은 데이터베이스에 남아 있습니다.
아래 예제들은 각 코드 블록이 독립적으로 완결되도록 Flask-SQLAlchemy 호출을 함수 안에 넣었습니다. 실제 프로젝트에서는 db와 Customer를 파일 앞부분에서 가져오거나 정의한 뒤, 같은 문장을 모듈 수준이나 뷰 안에서 실행하면 됩니다.
Flask-SQLAlchemy 설치 및 앱 구성
pip install flask-sqlalchemy로 패키지를 설치합니다(자세한 내용은 Flask-SQLAlchemy PyPI 페이지를 참조하세요). 그런 다음 SQLAlchemy 객체를 만들기 전에 SQLALCHEMY_DATABASE_URI를 설정해 Flask 앱이 사용할 데이터베이스를 지정합니다. 애플리케이션 팩터리를 사용하면 설정을 한곳에 모을 수 있고, 일반 모듈에서는 두 개의 import 문을 파일 맨 위에 두면 됩니다:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
def create_app():
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
db = SQLAlchemy(app)
return app, db
이 설정에는 모든 SQLAlchemy 데이터베이스 URL을 사용할 수 있습니다. 여기에 표시된 인메모리 SQLite URL은 예제를 독립적으로 실행할 수 있게 하기 위한 것이며, 실제 애플리케이션에서는 사용 중인 데이터베이스의 URL로 바꾸세요.
모델 정의 및 레코드 생성
삭제 작업에는 대상이 될 행이 필요하므로 먼저 작은 Customer 모델부터 만듭니다. db.Column으로 정의한 각 속성은 customer 테이블의 열이 됩니다. 실제 프로젝트에서는 이 클래스를 모델 파일의 모듈 수준에 두지만, 여기서는 예제가 독립적으로 동작하도록 팩터리 함수를 사용합니다:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
def define_models(db):
class Customer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
address = db.Column(db.String(200))
email = db.Column(db.String(50))
return Customer
def seed_first_customer(app, db):
Customer = define_models(db)
with app.app_context():
db.create_all()
db.session.add(
Customer(
name="Ada Lovelace",
address="12 Analytical Engine Way",
email="ada@example.com",
)
)
db.session.commit()
db.create_all()은 Flask 애플리케이션 컨텍스트 안에서 테이블을 만들고, db.session.add()와 db.session.commit()는 레코드를 삽입합니다. id가 기본 키이므로 행이 삽입될 때 데이터베이스가 자동으로 값을 할당하며, 이 값은 나중에 레코드를 조회할 때 사용할 수 있는 안정적인 식별자가 됩니다.
레코드 불러오기 및 삭제
db.session.get(Customer, customer_id)는 해당 기본 키의 인스턴스를 반환하고, 행이 없으면 None을 반환합니다. db.session.delete()에 None이 전달되지 않도록 먼저 레코드가 없는 경우를 확인하세요. 예제 코드가 반환하는 문자열은 코드와 바이트 단위로 동일하게 유지되므로 영어로 표시됩니다:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
def delete_customer(customer_id):
customer = db.session.get(Customer, customer_id)
if customer is None:
return "No customer with that id."
db.session.delete(customer)
db.session.commit()
return "Customer deleted."
db.session.delete()를 호출해도 행이 즉시 삭제되지는 않습니다. 이 호출은 인스턴스를 세션 안에서 삭제 예정으로 표시할 뿐이며, 변경 내용은 다음 db.session.commit()가 실행될 때까지 대기 상태로 남습니다.
삭제 커밋 및 실패 처리
db.session.commit()는 예약된 DELETE를 포함해 대기 중인 모든 변경을 하나의 트랜잭션으로 반영하고 결과를 영구적으로 저장합니다. 커밋을 생략하면 삭제가 데이터베이스에 반영되지 않습니다. db.session.commit()가 실행되기 전까지 변경은 트랜잭션 안에 대기 상태로 남습니다. 다음 예제를 실행해 일반 Python에서 삭제가 어떻게 영구적으로 반영되는지 확인해 보세요:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE customer (id INTEGER PRIMARY KEY, name TEXT)")
conn.execute("INSERT INTO customer VALUES (1, 'Ada')")
cursor = conn.cursor()
cursor.execute("DELETE FROM customer WHERE id = 1")
conn.commit()
print(cursor.execute("SELECT COUNT(*) FROM customer").fetchone()[0])
0
커밋은 제약 조건 위반이나 연결 끊김 같은 이유로 실패할 수도 있습니다. 이 경우 트랜잭션은 열린 상태로 남고, db.session.rollback()를 호출해 실패한 트랜잭션을 버리기 전까지 세션은 추가 쿼리를 실행할 수 없습니다:
def commit_changes():
try:
db.session.commit()
except Exception:
db.session.rollback()
raise
Flask-SQLAlchemy는 db.session의 범위를 애플리케이션 컨텍스트로 한정하므로, 다음 쿼리를 실행하기 전에 실패한 세션을 항상 롤백하거나 제거하세요.
전체 예제 및 결과 확인
지금까지의 내용을 종합하면, 다음 예제는 앱을 구성하고 Customer 모델을 정의한 뒤 레코드 하나를 삽입하고 삭제한 다음 다시 조회합니다. 이 예제를 실행하려면 Flask-SQLAlchemy가 설치되어 있어야 하며, 패키지가 있는 프로젝트에서 main()을 호출하세요:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
def main():
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
db = SQLAlchemy(app)
class Customer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
address = db.Column(db.String(200))
email = db.Column(db.String(50))
def insert_customer(name, address, email):
customer = Customer(name=name, address=address, email=email)
db.session.add(customer)
db.session.commit()
return customer.id
def delete_customer(customer_id):
customer = db.session.get(Customer, customer_id)
if customer is None:
return "No customer with that id."
db.session.delete(customer)
db.session.commit()
return "Customer deleted."
with app.app_context():
db.create_all()
new_id = insert_customer(
"Ada Lovelace", "12 Analytical Engine Way", "ada@example.com"
)
print(delete_customer(new_id))
print(db.session.get(Customer, new_id))
main()을 실행하면 스크립트는 Customer deleted.를 출력한 다음 None을 출력합니다. 이는 삭제된 기본 키로 후속 db.session.get() 호출을 수행해도 행을 찾지 못하기 때문입니다. 이 문자열은 코드 블록 안의 내용과 동일하게 영어로 출력됩니다. 실제 앱에서 삭제를 확인하려면 같은 방식으로 레코드를 다시 조회하거나 필터링된 개수 쿼리를 실행하세요. None 결과(또는 개수 0)는 행이 삭제되었음을 확인해 줍니다.