基于flask_sqlachemy的分頁(yè)

基于flask_sqlachemy的分頁(yè)

flask 項(xiàng)目中使用分頁(yè)paginate
報(bào)錯(cuò) AttributeError:Query object has no attribute 'paignate'
創(chuàng)建model的時(shí)候使用的是sqlachemy,而不是flask_sqlachemy,而分頁(yè)paginate方法只在flask_sqlchemy中提供码荔,同樣get_or_404()和first_or_404()也只在flask_sqlachemy中才有纤控,所以要想使用,必須修改model的創(chuàng)建方式纽哥,基于flask_sqlalchemy才可以逮光。

https://stackoverflow.com/questions/18409645/sqlalchemy-does-not-work-with-pagination
屬性:
items : 表示獲得的查詢結(jié)果
pages : 表示一共有多少頁(yè)
page :獲得當(dāng)前頁(yè)碼數(shù)
total :數(shù)據(jù)總條數(shù)
has_prev: 是否有上一頁(yè)
has_next: 是否有下一頁(yè)
prev_num:上一頁(yè)頁(yè)碼
next_num:下一頁(yè)頁(yè)碼
iter_page():當(dāng)前頁(yè)的頁(yè)碼列表

關(guān)聯(lián)查詢


data = db.session.query(Alipay_B_Order.spec_name, Alipay_B_Bus.app_id).\
    join(Alipay_B_Bus,Alipay_B_Order.bus_id == Alipay_B_Bus.bus_id).\
    filter(Alipay_B_Order.order_id == order_id).first()

query 中使用了 Alipay_B_Order和 Alipay_B_Bus代箭,后面再 join 一次 Alipay_B_Bus.那么 SQLAlchemy 如何選擇 JOIN 的時(shí)候誰(shuí)先誰(shuí)后呢?看看這個(gè)錯(cuò)誤就知道了:

data = db.session.query(Alipay_B_Order.spec_name, Alipay_B_Bus.app_id).\
    join(Alipay_B_Order,Alipay_B_Order.bus_id == Alipay_B_Bus.bus_id).\
    filter(Alipay_B_Order.order_id == order_id).first()

08-17 14:09 flask.app ERROR Exception on /internal_alipay/data_background_order_information [GET]
Traceback (most recent call last):
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/mnt/home/tangsq/3/AppletServers/app/internal_alipay_applet/data_background_api.py", line 72, in data_background_order_information
    join(Alipay_B_Order,Alipay_B_Order.bus_id == Alipay_B_Bus.bus_id).\
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 1964, in join
    from_joinpoint=from_joinpoint)
  File "<string>", line 2, in _join
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/sqlalchemy/orm/base.py", line 201, in generate
    fn(self, *args[1:], **kw)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2108, in _join
    outerjoin, full, create_aliases, prop)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2164, in _join_left_to_right
    l_info.selectable)
sqlalchemy.exc.InvalidRequestError: Can't join table/selectable 'alipay_b_order' to itself

select * from alipay_b_order a INNER JOIN alipay_b_bus b on a.bus_id = b.bus_id where order_id ="2021043009571900008c"

因此涕刚,query 中參數(shù)的順序很重要嗡综,query 的第一個(gè)參數(shù)就代表著第一個(gè)table是誰(shuí)。故杜漠,此處 JOIN 的目標(biāo)應(yīng)該是 Alipay_B_Bus极景, 而不應(yīng)該是 Alipay_B_Order。

聯(lián)表分頁(yè)


data = db.session.query(Alipay_B_Order).\
    join(Alipay_B_Bus,Alipay_B_Order.bus_id == Alipay_B_Bus.bus_id).\
    filter(Alipay_B_Order.order_id==order_id, Alipay_B_Bus.app_id == app_id).paginate(1, per_page=20, error_out=False)

08-17 15:17 flask.app ERROR Exception on /internal_alipay/data_background_order_information [GET]
Traceback (most recent call last):
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/root/.virtualenvs/apicall_env/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/mnt/home/tangsq/3/AppletServers/app/internal_alipay_applet/data_background_api.py", line 77, in data_background_order_information
    filter(Alipay_B_Order.order_id==order_id, Alipay_B_Bus.app_id == app_id).paginate(1, per_page=20, error_out=False)
AttributeError: 'Query' object has no attribute 'paginate'

報(bào)錯(cuò)的原因是db.session.query 默認(rèn)返回的是 orm.Query 對(duì)象驾茴,這個(gè)對(duì)象并不包含paginate方法盼樟。
要解決這個(gè)問(wèn)題:

方法一

修改 Flask-SQLAlchemy 的源碼

找到 SQLAlchemy 對(duì)象的 init 定義,在其中加入 session_options['query_cls'] = BaseQuery 即可:

def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None):

    if session_options is None:
        session_options = {}

    session_options.setdefault('scopefunc', connection_stack.__ident_func__)
    self.use_native_unicode = use_native_unicode
    self.app = app

    # 使用 BaseQuery锈至,這樣可以讓使用 db.session.query 等方法創(chuàng)建的 Query 對(duì)象支持 BaseQuery 的方法
    session_options['query_cls'] = BaseQuery

方法二

另一種關(guān)聯(lián)查詢語(yǔ)法

在 Flask-SQLAlchemy 提供的 Model 對(duì)象中晨缴,可以使用 Model.query 這樣的語(yǔ)法來(lái)直接得到一個(gè)查詢對(duì)象,這是由于 Flask-SQLAlchemy 中存在一個(gè) _QueryProperty 類峡捡,每次調(diào)用 Model.get時(shí)击碗,會(huì)自動(dòng)生成一個(gè)基于當(dāng)前 session 的 query 對(duì)象:

data = Alipay_B_Order.query.join(Alipay_B_Bus,Alipay_B_Order.bus_id == Alipay_B_Bus.bus_id).\
    filter(Alipay_B_Order.order_id==order_id, Alipay_B_Bus.app_id == app_id).\
    paginate(1, per_page=20, error_out=False)
print({"total": data.total,
                     "pages": data.pages,
                     "next_num": data.next_num,
                     "prev_num": data.prev_num,
                     "has_next": data.has_next,
                     "has_prev": data.has_prev})
print(data.items)
for item in data.items:
    oneData = item.__dict__
    del oneData["_sa_instance_state"]
    print(item.spec_name)

參考--在 Flask-SQLAlchemy 中聯(lián)表查詢

class BaseQuery(orm.Query):
    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None, count=True):
        """
        當(dāng)“error_out”為"True"時(shí),符合下面判斷時(shí)將引起404響應(yīng)们拙,我一般都關(guān)了這個(gè)
        error_out稍途,把接口返回什么的控制權(quán)拿回來(lái)。
        """
        //如果"page"或"per-page"是"None",則將從請(qǐng)求查詢
        //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: // 如果沒(méi)有請(qǐng)求分別默認(rèn)為1和20
            if page is None:
                page = 1
            if per_page is None:
                per_page = 20
        //如果指定了“max_per_page”械拍,則“per_page”將受這個(gè)值鉗制。
        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
        //原來(lái)paginate就是用的limit和offset装盯,我猜如果有重復(fù)數(shù)據(jù)坷虑,先limit后all,all的時(shí)候再去個(gè)重验夯,數(shù)據(jù)就少了猖吴,分頁(yè)也沒(méi)了
        items = self.limit(per_page).offset((page - 1) * per_page).all()
        ......
        //如果"count"是"False",total就不能用了,不用的時(shí)候可以關(guān)掉,省個(gè)查詢
        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代碼沒(méi)有去重摔刁,再看看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()
    ......
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绑谣,隨后出現(xiàn)的幾起案子党窜,更是在濱河造成了極大的恐慌,老刑警劉巖借宵,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幌衣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壤玫,警方通過(guò)查閱死者的電腦和手機(jī)豁护,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)欲间,“玉大人楚里,你說(shuō)我怎么就攤上這事×蕴” “怎么了班缎?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)她渴。 經(jīng)常有香客問(wèn)我达址,道長(zhǎng),這世上最難降的妖魔是什么趁耗? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任沉唠,我火速辦了婚禮,結(jié)果婚禮上苛败,老公的妹妹穿的比我還像新娘右冻。我一直安慰自己,他們只是感情好著拭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布纱扭。 她就那樣靜靜地躺著,像睡著了一般儡遮。 火紅的嫁衣襯著肌膚如雪乳蛾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天鄙币,我揣著相機(jī)與錄音肃叶,去河邊找鬼。 笑死十嘿,一個(gè)胖子當(dāng)著我的面吹牛因惭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绩衷,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蹦魔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼激率!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起勿决,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤乒躺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后低缩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘉冒,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年咆繁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讳推。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玩般,死狀恐怖娜遵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壤短,我是刑警寧澤设拟,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站久脯,受9級(jí)特大地震影響纳胧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帘撰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一跑慕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摧找,春花似錦核行、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至综苔,卻和暖如春惩系,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背如筛。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工堡牡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杨刨。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓晤柄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妖胀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芥颈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容