flask-sqlalchemy的paginate源码分析

贺经纶
2023-12-01

今天遇到一个问题,用flask-sqlalchemy的paginate做分页的时候,发现联表查询时分页出来结果数据少了很多,直接all()出来就没问题,把sql单独执行发现是联表查时有重复数据,group一下就好了,但是all()结果没有重复的,all()还给滤重了?而且paginate还是在滤重前做的limit,所以去重后结果就少了,而且还影响了total的值,趁这个机会看一下flask-sqlalchemy的源码吧

class BaseQuery(orm.Query):
	def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None, count=True):
        """
        当“error_out”为"True"时,符合下面判断时将引起404响应,我一般都关了这个
        error_out,把接口返回什么的控制权拿回来。
        """
        //如果"page"或"per-page"是"None",则将从请求查询
        //flask-sqlalchemy和flask app绑定的还真是挺深,连request都用上了
        if request:
            if page is None:
                try:
                	//这么写更好page = request.args.get('page', 1, type=int)
                    page = int(request.args.get('page', 1))
                except (TypeError, ValueError):
                    if error_out:
                        abort(404)

                    page = 1

            if per_page is None:
                try:
                    per_page = int(request.args.get('per_page', 20))
                except (TypeError, ValueError):
                    if error_out:
                        abort(404)

                    per_page = 20
        else: // 如果没有请求分别默认为1和20
            if page is None:
                page = 1
            if per_page is None:
                per_page = 20
		//如果指定了“max_per_page”,则“per_page”将受这个值钳制。
        if max_per_page is not None:
            per_page = min(per_page, max_per_page)

        if page < 1:
			......
                page = 1

        if per_page < 0:
			......
                per_page = 20
        //原来paginate就是用的limit和offset,我猜如果有重复数据,先limit后all,all的时候再去个重,数据就少了,分页也没了
        items = self.limit(per_page).offset((page - 1) * per_page).all()
		......
		//如果"count"是"False",total就不能用了,不用的时候可以关掉,省个查询
        if not count:
            total = None
        else:
        	//为什么order_by(None)?
            total = self.order_by(None).count()

        return Pagination(self, page, per_page, total, items)

class Pagination(object):
    def __init__(self, query, page, per_page, total, items):
        #: the unlimited query object that was used to create this
        #: pagination object.
        self.query = query
        #: the current page number (1 indexed)
        self.page = page
        #: the number of items to be displayed on a page.
        self.per_page = per_page
        #: the total number of items matching the query
        self.total = total
        #: the items for the current page
        self.items = items
    ......

flask-sqlalchemy代码没有去重,再看看sqlalchemy的代码

class Query(Generative):
	......
	def all(self):
        """
        不翻译了,留着原话,这里给去重了
        Return the results represented by this :class:`_query.Query`
        as a list.
        This results in an execution of the underlying SQL statement.
        .. warning::  The :class:`_query.Query` object,
           when asked to return either
           a sequence or iterator that consists of full ORM-mapped entities,
           will **deduplicate entries based on primary key**.  See the FAQ for
           more details.
            .. seealso::
                :ref:`faq_query_deduplicating`
        """
        return self._iter().all()
	......

到此,可知sqlalchemy的all()操作是去重了的

参考
https://github.com/pallets/flask-sqlalchemy
https://github.com/sqlalchemy/sqlalchemy

 类似资料: