1. 程式人生 > >Flask Web 開發 部落格文章_3

Flask Web 開發 部落格文章_3

繼續上一篇章,分頁顯示的問題

上一章節講的是生成了虛擬的使用者和文章

這裡繼續講分頁

這裡需要修改app/main/views.py函式

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

page是通過查詢字串字典裡面是否有'page'要求來生成對應的變數page,比如,你的url後面查詢字串裡面page=11,那就是要求瀏覽11頁

如果沒有,就預設從第一頁開始瀏覽,type=int這句我沒理解,我也沒查到get方法有第三個引數可選?不過看書上的字面意思是,當從page這個key取值取出來的內容無法轉化成證書時候,就使用預設引數,也就是1.

渲染的頁數從請求的查詢字串(request.args)中獲取,如果沒有明確指定,則預設渲染第一頁。引數type=int 保證引數無法轉換成整數時,返回預設值。
為了顯示某頁中的記錄,要把all() 換成Flask-SQLAlchemy 提供的paginate() 方法

。頁數是paginate() 方法的第一個引數,也是唯一必需的引數。可選引數per_page 用來指定
每頁顯示的記錄數量;如果沒有指定,則預設顯示20 個記錄。另一個可選引數為error_out,當其設為True 時(預設值),如果請求的頁數超出了範圍,則會返回404 錯誤;如果設為False,頁數超出範圍時會返回一個空列表。為了能夠很便利地配置每頁顯示的記錄數量,引數per_page 的值從程式的環境變數FLASKY_POSTS_PER_PAGE 中讀取。
這樣修改之後,首頁中的文章列表只會顯示有限數量的文章。若想檢視第2 頁中的文章,要在瀏覽器位址列中的URL 後加上查詢字串?page=2

-----------------------------------------------------------------------------插播原始碼

----------------------------------------------------------------------------------------------------

看到request.args.get('page',1,type=int)這句,甚是不理解,get方法不是隻有2個引數,key和default麼,怎麼會有第三個引數

思路一直在dict的get方法裡面找辦法,但是找了半天全是隻有2個引數的

後來其他筒子給我提醒找找request.args.get這個方法

後來去看了原始碼,發現args這個是個MultiDict,不是普通字典,而且他的get方法也確實不一樣,具體方法如下

就是他會通過第三個引數設定的型別,將第一個引數取到的值轉化成該型別,如果轉換失敗,則返回預設值,官方文件的例子很好地說明了第三個引數的用法

而關於MultiDict這個字典是什麼意思,先留個坑位,不展開講了

#

# 留給MultiDict

#

-----------------------------------------------------------------------------結束插播-------------------------------------------------------------------------------------------

首先是要建立一個巨集html檔案,以便其他頁面匯入使用,這個巨集檔案的主題內容就是設定分頁導航

app/templates/_macros.html

{% 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%}">«
		</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="disabled"><a href="#">…</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 %}">
			»
		</a>
	</li>
</ul>
{% endmacro %}

一步一步來講

首先是

{% macro pagination_widget(pagination,endpoint) %}

建立巨集檔案macro,其實和設定函式是一樣的,這裡的pagination_widget是函式名,後面括號內的是傳入的引數

隨後設定一個class類名是pagination的ul類

下面開始設定條件

{% if not pagination.has_prev %} 這句,在這之前,還需要再詳細講一下這個分頁如何實現的先後順序,自己理解的

首先,pagination 是 paginate方法通過Post.query查詢出來的分頁方法,paginate的第一個引數就是page引數,也就是使用者指定要看第幾頁,但是,當你第一次訪問的時候,就是預設的第一頁,因為你的request裡面是去到達這個整體頁面(比如你點選了profile,是導向到profile這個頁面)。

而pagination這個類物件,又通過per_page=current_app.config['FLASKY_POSTS_PER_PAGE']這個語句,來進行了分頁,比如,每頁顯示20的話,他分成了6頁。

那在最下面的導航欄,就會有1,2,3,4,5,6這樣的超連結,你再點選2,3,4,5,6時候,就會有page在request裡面

關於request裡面的查詢字串,這個感覺要補的東西太多,先挖個坑後面補吧

#

# 留給查詢字串

#

if判斷語句 對應的<li>,他並不是page的數字,而是對應的最左邊的箭頭也就是prevpage功能,相應的在最右邊也會有一個箭頭,對應的nextpage功能


{% if not pagination.has_prev %} 對當前頁面進行判斷

所以,如果你是在第一頁的話,因為沒有前一頁了,所以,會把最左邊的箭頭,禁用了,也就是class=disable見下圖1

反之,如果你的當前面也,有前一頁的,那麼,他會在url後面加上查詢字串,見下圖2



接著第二句

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

注意一下這裡的紅色字型部分,page-1,就是當前頁面號碼減去1,這就是左箭頭的作用,比如我當前頁面是在第三頁,那按左箭頭應該是導向第二頁,如下圖

同理,右箭頭的作用方式也是如此

另外,超連結最後的顯示字元 &laquo;  是表示左箭頭,而&raquo;  則表示右箭頭,具體的HTML特殊字元,可以參照下面連結

http://www.sjyhome.com/html/html-special-characters.html


接著分析程式碼

{% for p in pagination.iter_pages() %}

一個迭代器,返回一個在分頁導航中顯示的頁數列表。這個列表的最左邊顯示left_edge 頁,當前頁的左邊顯示left_current 頁,當前頁的右邊顯示right_current 頁,
最右邊顯示right_edge 頁。例如,在一個100 頁的列表中,當前頁為第50 頁,使用預設配置,這個方法會返回以下頁數:1、2、None、48、49、50、51、52、53、54、
55、None、99、100。None 表示頁數之間的間隔

 那也就是說,如果迭代出來的頁面就是當前的頁面的話,會有一個特殊效果,也就是如上面幾個畫面中顯示的,深色底色。 如果不是當前頁面的話,他就少一個深色底色,其他都一樣 {% else %}
<li class="disabled"><a href="#">&hellip;</a></li>
除了這些,也就是除了left_edge的2頁,left_current的2頁,right_current的5頁,還有right_edge的2頁,其他的顯示都應該是&hellip; 這個特殊字元,而這個特殊字元的顯示符號,是...  三個點,而這3個點的class是disable,是無法點選的,對應的連線地址也是"#"。
最後,右邊的箭頭和左邊的箭頭設定方法是一樣的,就不重複了。 然後在相對應的,有posts模板的地方,首先要引入macro,第二個再應用 比如在index.html
{% import "_macros.html" as macros %}
...
{% include "_posts.html" %}	
<div class="pagination">
	{{ macros.pagination_widget(pagination,'.index') }}
</div>

{% endif %}

以及在user.html裡面
{% include "_posts.html" %}

<div class="pagination">
	{{ macros.pagination_widget(pagination,'.user',username = user.username) }}
</div>

{% endif %}
這裡注意,在引入到user裡面的時候,記得要把變數username一起傳進去

Jinja2 巨集的引數列表中不用加入**kwargs 即可接收關鍵字引數。分頁巨集把接收到的所有關鍵字引數都傳給了生成分頁連結的url_for() 方法
注意這裡紅色字型部分,在url_for後面的引數,都會被傳入url_for的構成裡面,也就是說,變數會被傳入進去作為url_for的一部分,非常重要