1. 程式人生 > >Flask 學習筆記 分頁

Flask 學習筆記 分頁

今天開始是對Flask中分頁的學習,以下是檢視函式的程式碼:

@main.route('/',methods=['GET','POST'])
def index():
    form = PostForm()
    if form.validate_on_submit() and current_user.can(Permission.WRITE_ARTICLES):
        post = Post(body=form.body.data,author=current_user._get_current_object())
        db.session.add(post)
        return
redirect(url_for('main.index')) page = request.args.get('page',1,type=int) pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=15, error_out=False
) posts = pagination.items return render_template('index.html',form=form,posts=posts,pagination=pagination)

首先是之前主頁渲染髮表部落格的部分,從
page = request.args.get('page',1,type=int)
開始,是分頁的內容。
該語句中,request.args.get() 函式中,第一個引數為page,代表頁碼。
一開始看到這裡的時候其實我是很不理解的。這個‘page’是從哪來的呢?
後來我明白了,在訪問某個頁碼代表的網頁時,需要的url為

http://127.0.0.1:5000/?page=1

這裡的page便是剛剛函式中的第一個引數。
而第二個引數為預設值,當對首頁進行訪問的時候預設顯示第二個引數所代表的頁。

由於第三個引數中要求page的型別為整數,所以當我們輸出類似於:

http://127.0.0.1:5000/?page=3.5

這樣page為非整數的url時,不滿足type=int,所以頁面會自動返回預設值所代表的那一頁。

探討完了request,再說一下paginate()函式。對於

Post.query.order_by(Post.timestamp.desc()).paginate(page,
                                                                     per_page=15,
                                                                     error_out=False)

來說,前半段是將所有的博文按照時間戳降序排列,後半段的paginate()
函式中,第一個引數page是必須的引數,我們可以這樣理解:以上的這段程式碼返回了一個Paginate()物件,該物件有以下屬性:
這裡寫圖片描述
其中page是方法的第一個引數,也是唯一必需的引數,代表了物件的頁數。

per_page則是每頁的記錄,error_out負責在頁碼超出範圍之後進行報錯。

接下來就是比較核心的,也是不那麼易懂的部分了。

我們需要在jinja2模板中寫一個巨集(macro),個人理解的話,巨集在jinja2模板中的作用與函式在程式語言當中的作用十分類似,都有輸入輸出,然後都是將整體程式模組化的工具。

下面是分頁巨集的jinja2模板原始碼:

{% macro pagination_widget(pagination,endpoint) %}
<ul class="pagination">
<li {% if not pagination.has_prev %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_prev %}{{ url_for(endpoint,
        page=pagination.page-1,**kwargs) }}{% else %}#{% endif %}">
            &laquo;
        </a>
    </li>
    {% for p in pagination.iter_pages() %}{% if p %}{% if p==pagination.page %}
            <li class="active">
                <a href="{{ url_for(endpoint,page = p,**kwargs) }}">{{ p }}</a>
            </li>
            {% else %}
            <li>
                <a href="{{ url_for(endpoint,page = p,**kwargs) }}">{{ p }}</a>
            </li>
            {% endif %}{% else %}
        <li class="disbled"><a href="#">&hellip;</a> </li>
        {% endif %}{% endfor %}
    <li {% if not pagination.has_next %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_next %}{{ url_for(endpoint,
            page=pagination.page + 1,**kwargs) }}{% else %}#{% endif %}">
            &raquo;
        </a>
    </li>
</ul>
{% endmacro %}

首先從第一行開始看:

{% macro pagination_widget(pagination,endpoint) %}

以下是一些個人理解,如有錯誤歡迎指正!!~

個人通過與python中def函式的類比來進行理解,其中macro與def類似。

然後 pagination_widget相當於是巨集的名稱,也就相當於程式語言當中的函式名。括號中的兩個變數就相當於函式的位置引數。

然後

<ul class="pagination">
......
</ul>

來代表整個分頁部分的塊。

接下來是第一個部分:

<li {% if not pagination.has_prev %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_prev %}{{ url_for(endpoint,
        page=pagination.page-1,**kwargs) }}{% else %}#{% endif %}">
            &laquo;
        </a>
    </li>

我們從內往外分析:
最內層的程式碼:

<a href="{% if pagination.has_prev %}{{ url_for(endpoint,
        page=pagination.page-1,**kwargs) }}{% else %}#{% endif %}">
            &laquo;
        </a>

這段程式碼的目的是對‘上一頁’符號進行定義,&laquo;所代表的的是‘《’,代表一個上一頁符號。所以我們很容易就可以得到一個思路:上一頁不就是點了之後頁碼減一,並且當頁碼為1時停止功能麼?

那麼整個程式碼就好理解了,其中endpoint為引用母模板(也就是呼叫這個巨集命令的模板)的檢視函式的路由。

同樣的方法也可以應用於“下一頁“部分。

上面的部分對翻頁‘《》’的功能進行了操作,下面則對‘1234’等頁碼進行設定。

{% for p in pagination.iter_pages() %}{% if p %}{% if p==pagination.page %}
            <li class="active">
                <a href="{{ url_for(endpoint,page = p,**kwargs) }}">{{ p }}</a>
            </li>
            {% else %}
            <li>
                <a href="{{ url_for(endpoint,page = p,**kwargs) }}">{{ p }}</a>
            </li>
            {% endif %}{% else %}
        <li class="disbled"><a href="#">&hellip;</a> </li>
        {% endif %}{% endfor %}

其中第一行:pagination.iter_pages()會 產生如圖所示的基於頁數的列表:
這裡寫圖片描述
這個列表是動態產生的,不過邏輯沒有大的區別,其中:

{% if p==pagination.page %}
            <li class="active">

p如果與當前頁面的頁碼一致則會啟用,相應的圖示會變為深藍背景。

如果p!=pagination.page (也就是int),也就是說p現在是上圖中的省略號…
(… 代表省略號)那麼就給予一個disabled屬性。

介紹完了巨集如何編寫之後,接下來就剩下對巨集的引用了。

我們分別在首頁和使用者個人介面應用這個巨集來產生頁碼。由上文可知,使用這個巨集的時候需要傳入兩個引數,一是主要部分:pagination,二就是路由了。

需要注意的是,在首頁中,我們需要將所有的部落格顯示出來。而在個人的介面中,我們則需要顯示該使用者個人的部落格,因此在檢視函式中需要有所改變。
對於首頁,程式碼如下:

@main.route('/',methods=['GET','POST'])
def index():
    form = PostForm()
    if form.validate_on_submit() and current_user.can(Permission.WRITE_ARTICLES):
        post = Post(body=form.body.data,author=current_user._get_current_object())
        db.session.add(post)
        return redirect(url_for('main.index'))
    page = request.args.get('page',1,type=int)
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page,
                                                                     per_page=15,
                                                                     error_out=False)
    posts = pagination.items
    return render_template('index.html',form=form,posts=posts,pagination=pagination)

對於使用者介面,程式碼如下:

@main.route('/user/<username>')
def user(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        abort(404)
    page = request.args.get('page',1,int)
    pagination = Post.query.filter_by(author_id=user.id).paginate(page,per_page=5,error_out=False)
    posts = pagination.items
    return render_template('user.html',user=user,posts=posts,pagination=pagination)

其中不同的地方在於對pagination變數的賦值,一個是將所有的物件按時間排序賦給pagination,而另一個是通過username獲得了user.id再按照author_id進行篩選。

為什麼通過author_id進行篩選?在model.py中,我們在建立Post類時在User中有這麼一句引用:

posts = db.relationship('Post',backref=author,lazy='dynamic')

也就是說User和Post建立了聯絡,並在posts中建立了author屬性,只不過author屬性不直接顯示,而是通過author_id來進行體現。

其實之前通過進行關於Role和User的資料庫操作,我們已經有所察覺:

>>>admin = Role(name='admin')
>>>susan = User(username='susan',role = admin)

User當中,role這個屬性不直接顯示,而是通過role_id來體現
role_id是User的一個外來鍵,來與Role建立連線。

然後在模板當中,通過以下程式碼進行巨集的呼叫:

{{ macros.pagination_widget(pagination,'main.user',username=user.username) }}

該巨集是通過views.py中的user函式呼叫的,該函式的路由是這樣的:

@main.route('/user/<username>')

因此在使用巨集時,必須要給username一個名分,否則會報錯。

首頁的巨集的引用只需要將‘main.user’改成‘main.index’即可。