開發與維運

flask-sqlalchemy的paginate源碼分析

今天遇到一個問題,用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

Leave a Reply

Your email address will not be published. Required fields are marked *