본문 바로가기

# Development/Database

[SQLAlchemy] Model.query VS db.session.query()

Model.query  VS  db.session.query() 뭐가 다르지?

오늘 팀원분이 아래와 같은 질문를 주셨다.

sqlAlchemy에서 <model>.first()  쿼리로 조회된 데이터를
dict나 형태로 형변환을 해서 확인을 해보고 싶은데  왜 _asdict()가 안될까요🤔;;;
 

 

Flask에서 sqlalchemy를 사용하다보면  object.query와 session.query()를 편의에 따라 혼용해서 사용한다.

 

반환된 데이터를 조작하는 방법이 비슷해서 두 객체가 같을거라고 보통 생각하지만,

반환된 커리의 type을 로그로 찍어보면 사실 전혀 다른 객체임을 알 수 있다.

$ Model.query: <class 'app.model.Model'>
$ db.session.query: <class 'sqlalchemy.util._collections.result'>

 

Model.query와 db.session.query()의 차이점을 좀더 자세히 알아보자.

 

Model.query

이 방법은 주로 Flask SQLAlchemy에서 사용된다.

Flask SQLAlchemy는 Flask와 SQLAlchemy를 통합하는 라이브러리이다.


예를 들어 아래처럼 Flask SQLAlchemy의 Model을 통해 User라는 모델을 정의해보자.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy(app)

class User(db.Model):
    __tablename__ = 'users'  # 테이블 이름
    
    id = Column(Integer, primary_key=True)
    username = Column(String(50), unique=True)
    email = Column(String(100))
    
    def __init__(self, username, email):
        self.username = username
        self.email = email

 

Model은 Flask SQLAlchemy 모델 클래스를 나타내며, __tablename__ = 'users'로 설정하면 모델은 "users" 테이블과 연결 되고, SQLAlchemy는 해당 모델에 대한 쿼리 객체를 자동으로 생성한다.

 

이 쿼리 객체는 모델 클래스와 연결되고, 해당 테이블에 대한 쿼리를 수행할 때 아래와 같이 사용 할 수 있다. 

# User 모델을 대상으로 쿼리를 생성
user = User.query.filter_by(username='john').first()

 

db.session.query()

db.session.query() 메서드는 SQLAlchemy에서 일반적으로 쿼리를 생성하는 방법이다.

 

이 방법은 특정 모델과 관련이 없고, 쿼리 생성의 자유도가 높다.

db.session.query()를 사용하면 특정 모델 클래스가 아닌 데이터베이스 객체에 대한 쿼리를 생성할 수 있다.

 

명시적으로 세션을 생성한 다음 db.session.query(User)를 사용하여 User 테이블에 대한 쿼리를 생성한다.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy(app)

# User 모델이 아닌 특정 테이블에 대한 쿼리를 생성
user = db.session.query(User).filter_by(username='john').first()

 

어떤 방법이 더 선호될까?

작업 중인 모델이 하나뿐이라면 Model.query의 쿼리가 더 간결하여 사용하기 좋다.

하지만 여러 모델을 쿼리하거나 고급 쿼리 옵션을 필요로 하는 경우에는 db.session.query()를 사용해야 한다.

 

따라서 두가지 쿼리 방법 중 요구사항과 상황에 따라 알맞은 방법을 선택해서 사용하면 된다.

 

쿼리 결과 형변환

초반에 언급했듯이, 두개의 쿼리가 반환하는 데이터가 다르기 때문에 형 변환도 다르게 진행해야 한다.

 

Users.query.first()의 결과는 __dict__를 사용한다.

db.session.query(Users).first()의 결과는 _asdict()를 사용한다.

 

__dict__

__dict__ 는 파이썬에 내장된 매서드이다.

객체의 모든 속성을 가져와 dict로 변환하므로 데이터베이스 모델 객체에 정의되지 않은 속성 및 메서드도 가져오게된다.

 

Built-in Types

The following sections describe the standard types that are built into the interpreter. The principal built-in types are numerics, sequences, mappings, classes, instances and exceptions. Some colle...

docs.python.org

result = Users.query.filter_by(some_field='some_value').first()

# result 객체의 __dict__ 속성 사용
result_dict = result.__dict__

 

_asdict()

_asdict() 메서드는 SQLAlchemy에서 제공하는 메서드로, 

 SQLAlchemy 모델 객체를 collections.OrderedDict 형식의 dict로 변환한다.

 

Working with Engines and Connections — SQLAlchemy 1.4 Documentation

The caching feature requires that the dialect’s compiler produces SQL strings that are safe to reuse for many statement invocations, given a particular cache key that is keyed to that SQL string. This means that any literal values in a statement, such as

docs.sqlalchemy.org

$ db.session.query: <class 'sqlalchemy.util._collections.result'>
user = db.session.query(Users).first()
user_dict = user._asdict()

 

마무리

Model.query에서 _asdict()를 사용 할 수 없는 이유

_asdict() 메서드는 SQLAlchemy 객체의 메서드로, SQLAlchemy  인스턴스 또는 모델 객체에 대해 사용할 수 있다.

Model.query로 쿼리한 결과는 SQLAlchemy 모델 객체가 아닌 ORM 객체이므로  _asdict() 메서드를 사용할 수 없다.