SQLAlchemy, Flask에서 'AttributeError: 'int' object has no attribute '_sa_instance_state'' 오류 해결하기
SQLAlchemy, Flask에서 발생하는 'AttributeError: 'int' object has no attribute '_sa_instance_state'' 오류 해결
Flask 웹 애플리케이션에서 SQLAlchemy를 사용할 때 'int' object has no attribute '_sa_instance_state'
오류가 발생합니다. 이 오류는 일반적으로 Flask 뷰에서 데이터베이스 객체를 렌더링하려고 할 때 발생하며, 이는 객체가 더 이상 SQLAlchemy 세션에서 추적되지 않기 때문입니다.
해결 방법:
이 오류를 해결하려면 다음 방법 중 하나를 사용할 수 있습니다.
세션 유지:
뷰 함수에서 데이터베이스 객체를 가져온 후 렌더링하기 전에 세션을 유지해야 합니다. 이를 위해 다음과 같이 Session.expire_on_commit
플래그를 False
로 설정할 수 있습니다.
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(expire_on_commit=False)
또는 뷰 함수에서 직접 세션을 유지할 수 있습니다.
def my_view(session):
# 데이터베이스 객체를 가져옴
obj = session.query(MyModel).get(1)
# ...
# 렌더링하기 전에 세션 유지
session.expire_on_commit = False
return render_template('my_template.html', obj=obj)
프록시 객체 사용:
sa.orm.undetached
함수를 사용하여 프록시 객체를 만들 수 있습니다. 프록시 객체는 데이터베이스 객체를 나타내지만 SQLAlchemy 세션에서 추적되지 않습니다. 렌더링에 프록시 객체를 사용하면 이 오류를 방지할 수 있습니다.
from sqlalchemy import orm
def my_view():
# 데이터베이스 객체를 가져옴
obj = session.query(MyModel).get(1)
# 프록시 객체 만들기
proxy = orm.undetached(obj)
# ...
# 렌더링에 프록시 객체 사용
return render_template('my_template.html', obj=proxy)
__repr__ 메서드 재정의:
데이터베이스 객체의 __repr__
메서드를 재정의하여 원하는 문자열 표현을 제공할 수 있습니다. 이를 통해 템플릿에서 객체를 직접 렌더링할 수 있으며, 이 경우 _sa_instance_state
속성에 대한 오류가 발생하지 않습니다.
class MyModel(db.Model):
# ...
def __repr__(self):
return f"<MyModel id={self.id} name={self.name}>"
주의 사항:
Session.expire_on_commit
플래그를False
로 설정하면 성능 저하가 발생할 수 있으므로 주의해야 합니다.- 프록시 객체는 데이터베이스와 동기화되지 않으므로 주의해서 사용해야 합니다.
__repr__
메서드를 재정의하면 객체를 직접 렌더링할 수 있지만, 객체의 모든 속성이 포함된다는 보장은 없습니다.
예제 코드: SQLAlchemy, Flask에서 발생하는 'AttributeError: 'int' object has no attribute '_sa_instance_state'' 오류 해결
from flask import Flask, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine, expire_on_commit=False)
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# ...
# 렌더링하기 전에 세션 유지
session.expire_on_commit = False
return render_template('index.html', obj=obj)
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import orm
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine)
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# 프록시 객체 만들기
proxy = orm.undetached(obj)
# ...
# 렌더링에 프록시 객체 사용
return render_template('index.html', obj=proxy)
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine)
class MyModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
def __repr__(self):
return f"<MyModel id={self.id} name={self.name}>"
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# ...
# 렌더링에 객체 직접 사용
return render_template('index.html', obj=obj)
if __name__ == '__main__':
app.run(debug=True)
SQLAlchemy, Flask에서 발생하는 'AttributeError: 'int' object has no attribute '_sa_instance_state'' 오류 해결: 대체 방법
flask.jsonify 사용:
데이터베이스 객체를 JSON 형식으로 직접 변환하여 템플릿에 전달할 수 있습니다. 이를 위해 flask.jsonify
함수를 사용할 수 있습니다.
from flask import Flask, jsonify, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine)
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# 객체를 JSON 형식으로 변환
data = jsonify(obj)
# ...
# 렌더링에 JSON 데이터 사용
return render_template('index.html', data=data.json)
if __name__ == '__main__':
app.run(debug=True)
marshmallow 사용:
marshmallow
라이브러리를 사용하여 데이터베이스 객체를 직렬화하고 역직렬화할 수 있습니다. 이를 통해 객체를 원하는 형식으로 변환하고 템플릿에 전달할 수 있습니다.
from flask import Flask, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from marshmallow import Schema, fields
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine)
class MyModelSchema(Schema):
id = fields.Integer()
name = fields.String()
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# 스키마를 사용하여 객체 직렬화
schema = MyModelSchema()
data = schema.dump(obj)
# ...
# 렌더링에 직렬화된 데이터 사용
return render_template('index.html', data=data)
if __name__ == '__main__':
app.run(debug=True)
Jinja2 확장 사용:
Jinja2 확장을 사용하여 데이터베이스 객체를 직접 템플릿에서 렌더링할 수 있습니다. 이를 위해 사용자 정의 템플릿 태그를 만들 수 있습니다.
from flask import Flask, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from jinja2 import Markup
app = Flask(__name__)
engine = create_engine('sqlite:///mydatabase.db')
Session = sessionmaker(bind=engine)
app.jinja_env.add_extension('MyExtension')
class MyExtension(jinja2.Extension):
def __init__(self, app=None):
if app:
self.app = app
def render_object(self, env, obj):
# 객체를 원하는 형식으로 변환
html = f"<div>ID: {obj.id}</div><div>Name: {obj.name}</div>"
return Markup(html)
@app.route('/')
def index():
session = Session()
# 데이터베이스 객체 가져옴
obj = session.query(MyModel).get(1)
# ...
# 템플릿에서 객체 직접 렌더링
return render_template('index.html', obj=obj)
if __name__ == '__main__':
app.run(debug=True)
위에 제시된 방법 외에도 다양한 대체 방법이 있을 수 있습니다. 상황에 맞는 가장 적합한 방법을 선택하는 것이 중요합니다.
flask.jsonify
를 사용하면 객체의 모든 속성이 JSON 형식으로 변환되는 것은 아님을 알아야 합니다.marshmallow
를 사용하려면 추가 라이브러리를 설치해야 합니다.- Jinja2 확장을 사용하려면 사용자 정의 템플릿 태그를 작성해야 합니다
python sqlalchemy flask