flask實戰-我的博客-編寫博客前臺

編寫博客前臺

博客前臺須要開放給全部用戶,這裏包括顯示文章列表、博客信息、文章內容和評論等功能功能。html

 

分頁顯示文章列表

爲了在主頁顯示文章列表,咱們要先在渲染主頁模板的index視圖的數據庫中獲取全部文章記錄並傳入模板:數據庫

blueprints\blog.py:flask

 

from personalBlog.models import Post

@blog_bp.route('/')
def index():
    posts = Post.query.order_by(Post, timestamp.desc()).all()
    return render_template('blog/index.html', posts = posts)

 

在主頁模板中,咱們使用for語句迭代全部文章記錄,一次渲染文章標題、發表時間和正文,以下所示:bootstrap

personalBlog/templates/blog/index.html:渲染文章列表app

 

{% block content %}
    <div class="page-header">
        <h1 class="display-3">{{ admin.blog_title|default('Blog Title') }}</h1>
        <h4 class="text-muted">&nbsp;{{ admin.blog_sub_title|default('Blog Subtitle' }}</h4>
    </div>
    <div class="row">
        <div class="col-sm-8">
            {% if posts %}
                {% for post in posts %}
                    <h3 class="text-primary"><a href="{{ url_for('.show_post', post_id=post.id) }}">{{ post.title }}</a></h3>
                    <P>
                        {{ post.body|striptags|truncate }}
                        <small><a href="{{ url_for('.show_post', post_id=post.id) }}">Read More</a></small>
                    </P>
                    <small>
                        Comments:<a href="{{ url_for('.show_post', post_id=post.id) }}#comments">{{ post.comments|length }}</a>&nbsp;&nbsp
                        Category:<a
                            href="{{ url_for('.show_category', category_id=post.category.id }}">{{ post.category.name }}</a>
                        <span class="float-right">{{ moment(post.timestamp).format('LL') }}</span>
                    </small>
                    {% if not loop.last %}
                        <hr>
                    {% endif %}
                {% endfor %}
            {% else %}
                <div class="tip">
                    <h5>No posts yet.</h5>
                    {% if current_user.is_authenticated %}
                        <a href="{{ url_for('admin.new_post') }}">Write Now</a>
                    {% endif %}
                </div>
            {% endif %}
        </div>

 

這裏的.show_post是指blog藍本下的.show_post視圖函數,在藍本內部,是能夠簡寫的(省略藍本名稱)

在for循環的外層,咱們添加一個if判斷,若是posts不包含文章,就顯示一個「No posts」提示。若是當前用戶已經登陸,還會在提示文字下面顯示一個指向新建文章頁面的連接。a標籤將文章標題渲染爲連接,連接中包含文章正文對應的URL。函數

咱們隊文章正文使用了truncate過濾器,它會截取正文開頭一部分(默認爲255個字符)做爲文章摘要。在truncate過濾器中,默認的結束符號爲「...」,你可使用end關鍵字指定爲中文省略號「……」。爲了讓排版更統一,文章的正文摘要沒有使用safe過濾器,默認顯示無樣式的文章HTML源碼。咱們附加了striptags過濾器以濾掉文章正文中的HTML標籤。oop

 

在文章摘要後面,咱們還添加了一個指向文章頁面(show_post視圖)的Read More按鈕,一樣的,文章標題也添加了指向文章頁面的連接。另外,每個文章摘要下方會使用<hr>添加分隔線,咱們經過判斷loop.last的值來避免在最後一個條目後添加分隔線。post

 

flask run命令執行時就會執行app.run(),因此在腳本中能夠不寫app.run()。學習

 

 

由於已經生成了虛擬數據,包含50篇文章。如今運行程序,首頁會顯示一個很長的文章列表,根據建立的隨機日期排序,最新發表的排在上面,以下所示:url

 

 

開始時提示404錯誤,是由於建立app的時候,沒有正確註冊blog藍本,致使blog/index.html沒有被正確的路由

 

 

若是全部的文章都在主頁顯示,無疑將會延長頁面加載時間。並且用戶須要拖動滾動條來瀏覽文章,太長的網頁會讓人感到沮喪,從而下降用戶體驗度。更好的作法是對文章數據進行分頁處理,每一頁值顯示少許的文章,並在頁面底部顯示一個分頁導航條,用戶經過單擊分頁導航上的頁數按鈕來訪問其餘頁面的文章。Flask-SQLAlchemy提供了簡單的分頁功能,使用paginate()查詢方法能夠分頁獲取文章記錄,下面來學習如何使用。

 

一、獲取分頁記錄

添加分頁支持後的index視圖,以下所示

personalBlog/blueprints/blog.py:對文章記錄進行分頁處理

 

@blog_bp.route('/')
def index():
    page = request.args.get('page', 1, type = int) #從查詢字符串獲取當前頁數
    per_page = current_app.config['PERSONALBLOG_POST_PER_PAGE'] #每頁數量
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page = per_page) #分頁對象
    posts = pagination.items #當前頁數的記錄列表
    return render_template('blog/index.html', pagination = pagination, posts = posts)

 

爲了實現分頁,咱們把以前的查詢執行函數all()換成了paginate(),它接收的兩個最主要的參數分別用來決定把記錄分紅幾頁(per_page),返回哪一頁的記錄(page)。page參數表明當前請求的頁數,咱們從請求的查詢字符串(request.args)中獲取,若是沒有設置則使用默認值1,指定int類型能夠保證在參數類型錯誤時使用默認值;per_page參數設置每頁返回的記錄數量,爲了方便統一修改,這個值從配置變量PERSONAL_POST_PER_PAGE獲取。

 

另外,可選的error_out參數用於設置當查詢的頁數超出總頁數時的行爲。當error_out設爲True時,若是頁面超過最大值,page或per_page爲負數或非整數會返回404錯誤(默認值);若是設爲False則返回空記錄。可選的max_per_page參數則用來設置每頁數量的最大值。

若是沒有指定page和per_page參數,Flask-SQLAlchemy會自動從查詢字符串中獲取對應查詢參數(page和per_page)的值,若是沒有獲取到,默認的page值爲1,默認的per_page值爲20。

 

調用查詢方法paginate()會返回一個Pagination類實例,它包含分頁的信息,咱們將其稱爲分頁對象。

對這個pagination對象調用items屬性會以列表的形式返回對應頁數(默認爲第一頁)的記錄。在訪問這個URL時,若是在URL後附加了查詢參數page來指定頁數,例如127.0.0.1:5000/?page=2,這時發起請求調用items變量將會得到第二個10條記錄。

 

除了經過查詢字符串獲取頁數,還能夠直接將頁數做爲URL的一部分。下面的視圖函數就是將page做爲URL變量:

 

@blog_bp.route('/', defaults={'page':1})
@blog_bp.route('/page/<int:page>')
def index(page):
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=current_app.config['PERSONALBLOG_POST_PER_PAGE'])
    posts = pagination.items #當前頁數的記錄列表
    return render_template('blog/index.html', pagination = pagination, posts = posts)

 

第一個路由使用defaults字典爲page變量指定默認值,當訪問127.0.0.1:5000/時,page取默認值1,返回第一頁的記錄;當訪問127.0.0.1:5000/page/2時則會返回第2頁的記錄。注意,咱們爲URL規則中的page變量使用了int轉換器,以便接收正確的整形頁數值。

 

一、 渲染分頁導航部件

咱們不能讓用戶經過在URL中附加查詢字符串來實現分頁瀏覽,而是應該在頁面底部提供一個導航部件。這個分頁導航部件應該包含上一頁、下一頁、以及跳轉到每一頁的按鈕,每一個按鈕都包含指向主頁的URL,並且URL中都添加了對應的查詢參數page的值。使用paginate()方法時,它會返回一個Pagination類對象,這個類包含不少用於實現分頁導航的方法和屬性,咱們能夠用它來獲取全部關於分頁的信息,以下所示

Pagination類屬性:

 

對於博客來講,設置一個簡單的包含上一頁、下一頁按鈕的分頁部件就足夠了。

在視圖函數中,咱們將分頁對象pagination傳入模板,而後在模板中使用它提供的方法和屬性來構建分頁部件。爲了便於重用,咱們能夠建立一個pager()宏:

 

{% macro pager(pagination, fragment='') %}
<nav aria-label="Page navigation">
    <ul class="pagination">
        <li class="page-item {% if not pagination.has_prev %}disabled{% endif %}">
            <a class="page-link" href="{{ url_for(request.endpoint, page=pagination.prev_num, **kwargs) + fragment if pagination.has_prev }}">
                <span aria-hidden="true">&larr;</span>Newer
            </a>
        </li>
        <li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
            <a class="page-link" href="{{ url_for(request.endpoint, page=pagination.next_num, **kwargs) + fragment if pagination.has_next }}">
                Older <span aria-hidden="true">&rarr;</span>
            </a>
        </li>
    </ul>

</nav>
{% endmacro %}

 

這個宏接收分頁對象pagination和URL片斷以及其餘附加的關鍵字參數做爲參數。咱們根據pagination.has_prev和pagination.has_next屬性來選擇渲染按鈕的禁用狀態,若是這兩個屬性返回False,那麼就爲按鈕添加disabled類,同時會用#做爲a標籤中的URL。分頁按鈕中的URL使用request.endpoint獲取當前請求的端點,而查詢參數page的值從pagination.prev_num(上一頁的頁數)和pagination.next_num(下一頁的頁數)屬性獲取。

 

在使用時,從macros.html模板中導入並在須要顯示分頁導航的位置調用便可,傳入分頁對象做爲參數:

templates/blog/index.html:

 

{% from "macros.html" import pager %}

{% if posts %}
    {% for post in posts %}
        …
    {% endfor %}
    <div class="page-footer">{{ pager(pagination) }}</div>
把settings.py文件中的PERSONALBLOG_POST_PER_PAGE變量從10改成4
訪問127.0.0.1:5000

 

實際上,Bootstrap-Flask已經內置了一個包含一樣功能,並且提供更多自定義設置的render_pager()宏。除此以外,他還提供了一個render_pagination()宏,能夠用來渲染一個標準的Bootstrap Pagination分頁導航部件。這兩個宏的用法和咱們上面編寫的pager()宏基本相同,render_pagination()宏支持的經常使用參數以下表所示,惟一的區別是render_pager()宏沒有ellipses參數。

 

render_pagination()宏的經常使用參數:

 

在程序中咱們將使用這兩個宏來渲染分頁導航部件,他們要從boostrap/pagination.html模板中導入,好比:

index.html:

{% from 'bootstrap/pagination.html' import render_pagination %}

    {% for post in posts %}
        …
    {% endfor %}
    <div class="page-footer">{{ render_pagination(pagination) }}</div>

 

使用render_pagination()宏渲染後的標準分頁導航部件以下所示:

相關文章
相關標籤/搜索