在SQLAlchemy中Model.query和session.query(Model)的区别

  • Post author:
  • Post category:其他


今天在看以前的代码的时候发现了一个问题,就是在进行数据查询的时候有的地方用到的写法不一致,虽然处理的结果都没有问题,但是秉着一致的原则(也可能是强迫症…)还是决定将用法统一一下。

首先用到的两种写法分别是:

result = SomeModel.query.filter(…)

result = session.query(SomeModel).filter(…)

由于当时写代码的时候也没有太在意,今天回过头来看时,发现它们两个还是有些差别的,正好趁着这个机会查了一下它们的区别,在此总结一下,以便之后复习回忆。

1. 首先我们假定有这么一个需求:

我们需要 Table_A、Table_B、Table_C多个表关联查询,但是我们最终只需要Table_A中的所有字段,这时该怎样进行查询?


方式一:

result = session.query(Model_A.col1,Model_A.col2... ...,Model_B).join(Model_B,Model_B.id == Model_A.id).filter(...)

这样你只需要把要查询的所有字段全部明确列出即可,但是这样可能会有个问题,如果表中的字段特别多的话那是不是要在这里写一大堆东西,这岂不是很麻烦?这时使用第二种方式会方便很多。


方式二:

result = Model_A.query.join(Model_B,Model_B.id == Model_A.id).filter(...)

这种方式会返回一个Model_A的对象,包含该表中所有字段,不包含其他表中的任何字段,而且你不需要再将需要的字段一一列出,是不是方便很多。

当然,你一可以根据需要只查询指定的字段,这时你可以使用下面这种方式:


查询指定字段:

result = Model_A.query.join(Model_B,Model_B.id == Model_A.id).filter(...).with_entities(Model_A.col1, Model_A.col2,Model_B.col1,Model_B.col2)


2.源码解析:

在 Flask-SQLAlchemy 提供的 Model 对象中,可以使用 Model.query 这样的语法来直接得到一个查询对象,这是由于 Flask-SQLAlchemy 中存在一个 _QueryProperty 类,每次调用 Model.get 时,会自动生成一个基于当前 session 的 query 对象。

// 模型
class CircleClasscircle(db.Model):
		... ...

// db.Model
class SQLAlchemy(object):
		    def __init__(self, app=None, use_native_unicode=True, session_options=None,
                 metadata=None, query_class=BaseQuery, model_class=Model,
                 engine_options=None):

		        self.use_native_unicode = use_native_unicode
		        self.Query = query_class
		        self.session = self.create_scoped_session(session_options)
		        self.Model = self.make_declarative_base(model_class, metadata)
		        self._engine_lock = Lock()
		        self.app = app
		        self._engine_options = engine_options or {}
		        _include_sqlalchemy(self, query_class)

		        if app is not None:
		            self.init_app(app)

		def make_declarative_base(self, model, metadata=None):
				... ... 
				... ...
				model.query = _QueryProperty(self)
        		return model

class _QueryProperty(object):
    def __init__(self, sa):
        self.sa = sa

    def __get__(self, obj, type):
        try:
            mapper = orm.class_mapper(type)
            if mapper:
                return type.query_class(mapper, session=self.sa.session())
        except UnmappedClassError:
            return None

3.案例解析:

// 部分代码,打印原生sql语句
@bp.route("/")
def test():
    baby_id = '57723c14cf7a42e89dbc694b98231e68'

    sql = CircleClasscircle.query.join(
        CircleStory, CircleClasscircle.id == CircleStory.circle_id).filter(
        CircleStory.baby_id == baby_id)

    sql2 = CircleClasscircle.query.join(
        CircleStory,
        CircleClasscircle.id == CircleStory.circle_id).filter(
        CircleStory.baby_id == baby_id).with_entities(
            CircleClasscircle.id,
        CircleClasscircle.baby_id)

    sql3 = db.session.query(CircleClasscircle,
        CircleStory).join(CircleStory,
        CircleClasscircle.id == CircleStory.circle_id).filter(
        CircleStory.baby_id == baby_id).order_by(
        CircleClasscircle.create_date.desc())

	sql4 = db.session.query(CircleClasscircle,
        CircleStory).join(CircleStory,
        CircleClasscircle.id == CircleStory.circle_id).filter(
        CircleStory.baby_id == baby_id).order_by(
        CircleClasscircle.create_date.desc()).first()

    print(sql)
    print("===================")
    print(sql2)
    print("===================")
    print(sql3)

	# sql4的查询结果包含两个表中的所有字段,要取结果集中的值通过下面的方法
    return sql4.CircleClasscircle.class_id + "--" + \
           str(sql4.CircleClasscircle.create_date) +  \
           sql4.CircleStory.is_read


打印sql语句注意点:


不要在检索语句的最后添加.all()或.first()等类似的方法,否则打印SQL语句失败。

// 打印失败(不要这样写)
 sql = CircleClasscircle.query.join(CircleStory, CircleClasscircle.id==CircleStory.circle_id).filter(... ...).all()   
 print(sql)
 
// 打印结果:[<CircleClasscircle c31731546399969124>] ,打印的是查询结果


sql1、sql2、sql3的打印结果

// 1.打印出的sql1的结果: 检索结果是CircleClasscircle表中的所有字段
SELECT
	classcircle.id AS id,
	... ... 省略
FROM
	classcircle
	INNER JOIN story ON classcircle.id = story.circle_id 
WHERE
	story.baby_id = % ( baby_id_1 ) s

// 2.打印出的sql2的结果: 检索结果是指定的字段
SELECT
	classcircle.id AS classcircle_id,
	classcircle.baby_id AS classcircle_baby_id 
FROM
	classcircle
	INNER JOIN story ON classcircle.id = story.circle_id 
WHERE
	story.baby_id = % ( baby_id_1 ) s
	
// 3.打印出的sql3的结果:检索结果是CircleClasscircle和CircleStory两个表中的所有字段
SELECT
	classcircle.id AS classcircle_id,
	... ... 省略
	story.id AS story_id,
	... ... 省略
FROM
	classcircle
	INNER JOIN story ON classcircle.id = story.circle_id 
WHERE
	story.baby_id = % ( baby_id_1 ) s 
ORDER BY
	classcircle.create_date DESC
	



版权声明:本文为ieeso原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。