Flask 教程 第九章:分頁

百度云搜索省核,搜各種資料:http://bdy.lqkweb.com

搜網(wǎng)盤迁杨,搜各種資料:http://www.swpan.cn

本文翻譯自The Flask Mega-Tutorial Part IX: Pagination

這是Flask Mega-Tutorial系列的第九部分,我將告訴你如何對數(shù)據(jù)列表進行分頁玩焰。

第八章我已經(jīng)做了幾個數(shù)據(jù)庫更改,以支持在社交網(wǎng)絡(luò)非常流行的“粉絲”機制。 有了這個功能拯田,接下來我準備好刪除一開始就使用的模擬用戶動態(tài)了。 在本章中甩十,應(yīng)用將開始接受來自用戶的動態(tài)更新船庇,并將其發(fā)布到網(wǎng)站首頁和個人主頁。

本章的GitHub鏈接為:Browse, Zip, Diff.

發(fā)布用戶動態(tài)

讓我們從簡單的事情開始吧侣监。 首頁需要有一個表單鸭轮,用戶可以在其中鍵入新動態(tài)。 我創(chuàng)建一個表單類:

class PostForm(FlaskForm):
    post = TextAreaField('Say something', validators=[
        DataRequired(), Length(min=1, max=140)])
    submit = SubmitField('Submit')

然后橄霉,我將該表單添加到網(wǎng)站首頁的模板中:

{% extends "base.html" %}

{% block content %}
    <h1>Hi, {{ current_user.username }}!</h1>
    <form action="" method="post">
        {{ form.hidden_tag() }}
        <p>
            {{ form.post.label }}<br>
            {{ form.post(cols=32, rows=4) }}<br>
            {% for error in form.post.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.submit() }}</p>
    </form>
    {% for post in posts %}
    <p>
    {{ post.author.username }} says: <b>{{ post.body }}</b>
    </p>
    {% endfor %}
{% endblock %}

模板中的變更和處理以前的表單類似窃爷。最后的部分是將表單處理邏輯添加到視圖函數(shù)中:

from app.forms import PostForm
from app.models import Post

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    form = PostForm()
    if form.validate_on_submit():
        post = Post(body=form.post.data, author=current_user)
        db.session.add(post)
        db.session.commit()
        flash('Your post is now live!')
        return redirect(url_for('index'))
    posts = [
        {
            'author': {'username': 'John'},
            'body': 'Beautiful day in Portland!'
        },
        {
            'author': {'username': 'Susan'},
            'body': 'The Avengers movie was so cool!'
        }
    ]
    return render_template("index.html", title='Home Page', form=form,
                           posts=posts)

我們來一個個地解讀該視圖函數(shù)的變更:

  • 導(dǎo)入PostPostForm
  • 關(guān)聯(lián)到index視圖函數(shù)的兩個路由都新增接受POST請求,以便視圖函數(shù)處理接收的表單數(shù)據(jù)
  • 處理表單的邏輯會為post表插入一條新的數(shù)據(jù)
  • 模板新增接受form對象姓蜂,以便渲染文本輸入框

在繼續(xù)之前按厘,我想提一些與Web表單處理相關(guān)的重要內(nèi)容。 請注意钱慢,在處理表單數(shù)據(jù)后逮京,我通過發(fā)送重定向到主頁來結(jié)束請求。 我可以輕松地跳過重定向束莫,并允許函數(shù)繼續(xù)向下進入模板渲染部分懒棉,因為這已經(jīng)是主頁視圖函數(shù)了。

那么览绿,為什么重定向呢策严? 通過重定向來響應(yīng)Web表單提交產(chǎn)生的POST請求是一種標準做法。 這有助于緩解在Web瀏覽器中執(zhí)行刷新命令的煩惱饿敲。 當你點擊刷新鍵時妻导,所有的網(wǎng)頁瀏覽器都會重新發(fā)出最后的請求。 如果帶有表單提交的POST請求返回一個常規(guī)的響應(yīng),那么刷新將重新提交表單倔韭。 因為這不是預(yù)期的行為暑脆,所以瀏覽器會要求用戶確認重復(fù)的提交,但是大多數(shù)用戶卻很難理解瀏覽器詢問的內(nèi)容狐肢。不過添吗,如果一個POST請求被重定向響應(yīng),瀏覽器現(xiàn)在被指示發(fā)送GET請求來獲取重定向中指定的頁面份名,所以現(xiàn)在最后一個請求不再是’POST’請求了碟联, 刷新命令就能以更可預(yù)測的方式工作。

這個簡單的技巧叫做Post/Redirect/Get模式僵腺。 它避免了用戶在提交網(wǎng)頁表單后無意中刷新頁面時插入重復(fù)的動態(tài)鲤孵。

展示用戶動態(tài)

如果你還記得,我創(chuàng)建過幾條模擬的用戶動態(tài)辰如,展示在主頁已經(jīng)有一段時間了普监。 這些模擬對象是在index視圖函數(shù)中顯式創(chuàng)建的一個簡單的Python列表:

    posts = [
        { 
            'author': {'username': 'John'}, 
            'body': 'Beautiful day in Portland!' 
        },
        { 
            'author': {'username': 'Susan'}, 
            'body': 'The Avengers movie was so cool!' 
        }
    ]

但是現(xiàn)在我在User模型中有了followed_posts()方法,它可以返回給定用戶希望看到的用戶動態(tài)的查詢結(jié)果集琉兜。 所以現(xiàn)在我可以用真正的用戶動態(tài)替換模擬的用戶動態(tài):

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    # ...
    posts = current_user.followed_posts().all()
    return render_template("index.html", title='Home Page', form=form,
                           posts=posts)

User類的followed_posts方法返回一個SQLAlchemy查詢對象凯正,該對象被配置為從數(shù)據(jù)庫中獲取用戶感興趣的用戶動態(tài)。 在這個查詢中調(diào)用all()會觸發(fā)它的執(zhí)行豌蟋,返回值是包含所有結(jié)果的列表廊散。 所以我最終得到了一個與我迄今為止一直使用的模擬用戶動態(tài)非常相似的結(jié)構(gòu)。 它們非常接近梧疲,模板甚至不需要改變允睹。

更容易地發(fā)現(xiàn)和關(guān)注用戶

相信你已經(jīng)留意到了,應(yīng)用沒有一個很好的途徑來讓用戶可以找到其他用戶進行關(guān)注幌氮。實際上缭受,現(xiàn)在根本沒有辦法在頁面上查看到底有哪些用戶存在。我將會使用少量簡單的變更來解決這個問題该互。

我將會創(chuàng)建一個新的“發(fā)現(xiàn)”頁面米者。該頁面看起來像是主頁,但是卻不是只顯示已關(guān)注用戶的動態(tài)慢洋,而是展示所有用戶的全部動態(tài)塘雳。新增的發(fā)現(xiàn)視圖函數(shù)如下:

@app.route('/explore')
@login_required
def explore():
    posts = Post.query.order_by(Post.timestamp.desc()).all()
    return render_template('index.html', title='Explore', posts=posts)

你有沒有注意到這個視圖函數(shù)中的奇怪之處陆盘? render_template()引用了我在應(yīng)用的主頁面中使用的index.html模板普筹。 這個頁面與主頁非常相似,所以我決定重用這個模板隘马。 但與主頁不同的是太防,在發(fā)現(xiàn)頁面不需要一個發(fā)表用戶動態(tài)表單,所以在這個視圖函數(shù)中,我沒有在模板調(diào)用中包含form參數(shù)蜒车。

要防止index.html模板在嘗試呈現(xiàn)不存在的Web表單時崩潰讳嘱,我將添加一個條件,只在傳入表單參數(shù)后才會呈現(xiàn)該表單:

{% extends "base.html" %}

{% block content %}
    <h1>Hi, {{ current_user.username }}!</h1>
    {% if form %}
    <form action="" method="post">
        ...
    </form>
    {% endif %}
    ...
{% endblock %}

該頁面也需要添加到導(dǎo)航欄中:

        <a href="{{ url_for('explore') }}">Explore</a>

還記得我在第六章中介紹的用于個人主頁渲染用戶動態(tài)的_post.html子模板嗎酿愧? 這是一個包含在個人主頁模板中的小模板沥潭,它獨立于其他模板,因此也可以被這些模板調(diào)用嬉挡。 我現(xiàn)在要做一個小小的改進钝鸽,將用戶動態(tài)作者的用戶名顯示為一個鏈接:

    <table>
        <tr valign="top">
            <td><img src="{{ post.author.avatar(36) }}"></td>
            <td>
                <a href="{{ url_for('user', username=post.author.username) }}">
                    {{ post.author.username }}
                </a>
                says:<br>{{ post.body }}
            </td>
        </tr>
    </table>

然后在主頁和發(fā)現(xiàn)頁中使用這個子模板來渲染用戶動態(tài):

    ...
    {% for post in posts %}
        {% include '_post.html' %}
    {% endfor %}
    ...

子模板期望存在一個名為post的變量,才能正常工作庞钢。該變量是上層模板中通過循環(huán)產(chǎn)生的拔恰。

通過這些細小的變更,應(yīng)用的用戶體驗得到了大大的提升』ǎ現(xiàn)在颜懊,用戶可以訪問發(fā)現(xiàn)頁來查看陌生用戶的動態(tài),并通過這些用戶動態(tài)來關(guān)注用戶风皿,而需要的操作僅僅是點擊用戶名跳轉(zhuǎn)到其個人主頁并點擊關(guān)注鏈接河爹。令人嘆為觀止!對吧桐款?

此時昌抠,我建議你在應(yīng)用上再次嘗試一下這個功能,以便體驗最后的用戶接口的完善鲁僚。

用戶動態(tài)

用戶動態(tài)的分頁

應(yīng)用看起來更完善了炊苫,但是在主頁顯示所有用戶動態(tài)遲早會出問題。如果一個用戶有成千上萬條關(guān)注的用戶動態(tài)時冰沙,會發(fā)生什么侨艾?你可以想象得到,管理這么大的用戶動態(tài)列表將會變得相當緩慢和低效拓挥。

為了解決這個問題唠梨,我會將用戶動態(tài)進行分頁。這意味著一開始顯示的只是所有用戶動態(tài)的一部分侥啤,并提供鏈接來訪問其余的用戶動態(tài)当叭。Flask-SQLAlchemy的paginate()方法原生就支持分頁。例如盖灸,我想要獲取用戶關(guān)注的前20個動態(tài)蚁鳖,我可以將all()結(jié)束調(diào)用替換成如下的查詢:

>>> user.followed_posts().paginate(1, 20, False).items

Flask-SQLAlchemy的所有查詢對象都支持paginate方法,需要輸入三個參數(shù)來調(diào)用它:

  • 從1開始的頁碼
  • 每頁的數(shù)據(jù)量
  • 錯誤處理布爾標記赁炎,如果是True醉箕,當請求范圍超出已知范圍時自動引發(fā)404錯誤。如果是False,則會返回一個空列表讥裤。

paginate方法返回一個Pagination的實例放棒。其items屬性是請求內(nèi)容的數(shù)據(jù)列表。Pagination實例還有一些其他用途己英,我會在之后討論间螟。

現(xiàn)在想想如何在index()視圖函數(shù)展現(xiàn)分頁呢。我先來給應(yīng)用添加一個配置項损肛,以表示每頁展示的數(shù)據(jù)列表長度吧寒亥。

class Config(object):
    # ...
    POSTS_PER_PAGE = 3

存儲這些應(yīng)用范圍的“可控機關(guān)”到配置文件是一個好主意,因為這樣我調(diào)整時只需去一個地方荧关。 在最終的應(yīng)用中溉奕,每頁顯示的數(shù)據(jù)將會大于三,但是對于測試而言忍啤,使用小數(shù)字很方便加勤。

接下來,我需要決定如何將頁碼并入到應(yīng)用URL中同波。 一個相當常見的方法是使用查詢字符串參數(shù)來指定一個可選的頁碼鳄梅,如果沒有給出則默認為頁面1。 以下是一些示例網(wǎng)址未檩,顯示了我將如何實現(xiàn)這一點:

要訪問查詢字符串中給出的參數(shù)冤狡,我可以使用Flask的request.args對象孙蒙。 你已經(jīng)在第五章中看到了這種方法,我用Flask-Login實現(xiàn)了用戶登錄的可以包含一個next查詢字符串參數(shù)的URL悲雳。

給主頁和發(fā)現(xiàn)頁的視圖函數(shù)添加分頁的代碼變更如下:

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    # ...
    page = request.args.get('page', 1, type=int)
    posts = current_user.followed_posts().paginate(
        page, app.config['POSTS_PER_PAGE'], False)
    return render_template('index.html', title='Home', form=form,
                           posts=posts.items)

@app.route('/explore')
@login_required
def explore():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.order_by(Post.timestamp.desc()).paginate(
        page, app.config['POSTS_PER_PAGE'], False)
    return render_template("index.html", title='Explore', posts=posts.items)

通過這些更改挎峦,這兩個路由決定了要顯示的頁碼,可以從page查詢字符串參數(shù)獲得或是默認值1合瓢。然后使用paginate()方法來檢索指定范圍的結(jié)果坦胶。 決定頁面數(shù)據(jù)列表大小的POSTS_PER_PAGE配置項是通過app.config對象中獲取的。

請注意晴楔,這些更改非常簡單顿苇,每次更改都只會影響很少的代碼。 我試圖在編寫應(yīng)用每個部分的時候税弃,不做任何有關(guān)其他部分如何工作的假設(shè)纪岁,這使我可以編寫更易于擴展和測試的且兼具模塊化和健壯性的應(yīng)用,并且不太可能失敗或出現(xiàn)BUG钙皮。

來嘗試下分頁功能吧蜂科。 首先確保你有三條以上的用戶動態(tài)顽决。 在發(fā)現(xiàn)頁面中更方便測試短条,因為該頁面顯示所有用戶的動態(tài)导匣。 你現(xiàn)在只會看到最近的三條用戶動態(tài)。 如果你想看接下來的三條茸时,請在瀏覽器的地址欄中輸入http://localhost:5000/explore?page=2贡定。

分頁導(dǎo)航

接下來的改變是在用戶動態(tài)列表的底部添加鏈接,允許用戶導(dǎo)航到下一頁或上一頁可都。 還記得我曾提到過paginate()的返回是Pagination類的實例嗎缓待? 到目前為止,我已經(jīng)使用了此對象的items屬性渠牲,其中包含為所選頁面檢索的用戶動態(tài)列表旋炒。 但是這個分頁對象還有一些其他的屬性在構(gòu)建分頁鏈接時很有用:

  • has_next: 當前頁之后存在后續(xù)頁面時為真
  • has_prev: 當前頁之前存在前置頁面時為真
  • next_num: 下一頁的頁碼
  • prev_num: 上一頁的頁碼

有了這四個元素,我就可以生成上一頁和下一頁的鏈接并將其傳入模板以渲染:

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    # ...
    page = request.args.get('page', 1, type=int)
    posts = current_user.followed_posts().paginate(
        page, app.config['POSTS_PER_PAGE'], False)
    next_url = url_for('index', page=posts.next_num) \
        if posts.has_next else None
    prev_url = url_for('index', page=posts.prev_num) \
        if posts.has_prev else None
    return render_template('index.html', title='Home', form=form,
                           posts=posts.items, next_url=next_url,
                           prev_url=prev_url)

 @app.route('/explore')
 @login_required
 def explore():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.order_by(Post.timestamp.desc()).paginate(
        page, app.config['POSTS_PER_PAGE'], False)
    next_url = url_for('explore', page=posts.next_num) \
        if posts.has_next else None
    prev_url = url_for('explore', page=posts.prev_num) \
        if posts.has_prev else None
    return render_template("index.html", title='Explore', posts=posts.items,
                          next_url=next_url, prev_url=prev_url)

這兩個視圖函數(shù)中的next_urlprev_url只有在該方向上存在一個頁面時签杈,才會被設(shè)置為由url_for()返回的URL瘫镇。 如果當前頁面位于用戶動態(tài)集合的末尾或者開頭,那么Pagination實例的has_nexthas_prev屬性將為’False’答姥,在這種情況下铣除,將設(shè)置該方向的鏈接為None

url_for()函數(shù)的一個有趣的地方是鹦付,你可以添加任何關(guān)鍵字參數(shù)尚粘,如果這些參數(shù)的名字沒有直接在URL中匹配使用,那么Flask將它們設(shè)置為URL的查詢字符串參數(shù)敲长。

現(xiàn)在讓我們把它們渲染在index.html模板上郎嫁,就在用戶動態(tài)列表的正下方:

    ...
    {% for post in posts %}
        {% include '_post.html' %}
    {% endfor %}
    {% if prev_url %}
    <a href="{{ prev_url }}">Newer posts</a>
    {% endif %}
    {% if next_url %}
    <a href="{{ next_url }}">Older posts</a>
    {% endif %}
    ...

主頁和發(fā)現(xiàn)頁都添加了分頁鏈接。第一個鏈接標記為“Newer posts”祈噪,并指向前一頁(請記住行剂,我顯示的用戶動態(tài)按時間的倒序來排序,所以第一頁是最新的內(nèi)容)钳降。 第二個鏈接標記為“Older posts”厚宰,并指向下一頁的帖子。 如果這兩個鏈接中的任何一個都是None遂填,則通過條件過濾將其從頁面中省略铲觉。

分頁

個人主頁中的分頁

主頁分頁已經(jīng)完成,但是吓坚,個人主頁中也有一個用戶動態(tài)列表撵幽,其中只顯示個人主頁擁有者的動態(tài)。 為了保持一致礁击,個人主頁也應(yīng)該實現(xiàn)分頁盐杂,以匹配主頁的分頁樣式逗载。

我開始更新個人主頁視圖函數(shù),其中仍然有一個模擬用戶動態(tài)的列表链烈。

@app.route('/user/<username>')
@login_required
def user(username):
    user = User.query.filter_by(username=username).first_or_404()
    page = request.args.get('page', 1, type=int)
    posts = user.posts.order_by(Post.timestamp.desc()).paginate(
        page, app.config['POSTS_PER_PAGE'], False)
    next_url = url_for('user', username=user.username, page=posts.next_num) \
        if posts.has_next else None
    prev_url = url_for('user', username=user.username, page=posts.prev_num) \
        if posts.has_prev else None
    return render_template('user.html', user=user, posts=posts.items,
                           next_url=next_url, prev_url=prev_url)

為了得到用戶的動態(tài)列表厉斟,我利用了User模型中已經(jīng)定義好的user.posts一對多關(guān)系。 我執(zhí)行該查詢并添加一個order_by()子句强衡,以便我首先得到最新的用戶動態(tài)擦秽,然后完全按照我對主頁和發(fā)現(xiàn)頁面中的用戶動態(tài)所做的那樣進行分頁。 請注意漩勤,由url_for()函數(shù)生成的分頁鏈接需要額外的username參數(shù)感挥,因為它們指向個人主頁,個人主頁依賴用戶名作為URL的動態(tài)組件越败。

最后触幼,對user.html模板的更改與我在主頁上所做的更改相同:

    ...
    {% for post in posts %}
        {% include '_post.html' %}
    {% endfor %}
    {% if prev_url %}
    <a href="{{ prev_url }}">Newer posts</a>
    {% endif %}
    {% if next_url %}
    <a href="{{ next_url }}">Older posts</a>
    {% endif %}

完成對分頁功能的實驗后,可以將POSTS_PER_PAGE配置項設(shè)置為更合理的值:

class Config(object):
    # ...
    POSTS_PER_PAGE = 25
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末究飞,一起剝皮案震驚了整個濱河市置谦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌噪猾,老刑警劉巖霉祸,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袱蜡,居然都是意外死亡丝蹭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門坪蚁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奔穿,“玉大人,你說我怎么就攤上這事敏晤〖铮” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵嘴脾,是天一觀的道長男摧。 經(jīng)常有香客問我,道長译打,這世上最難降的妖魔是什么耗拓? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮奏司,結(jié)果婚禮上乔询,老公的妹妹穿的比我還像新娘。我一直安慰自己韵洋,他們只是感情好竿刁,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布黄锤。 她就那樣靜靜地躺著,像睡著了一般食拜。 火紅的嫁衣襯著肌膚如雪鸵熟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天监婶,我揣著相機與錄音旅赢,去河邊找鬼齿桃。 笑死惑惶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的短纵。 我是一名探鬼主播带污,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼香到!你這毒婦竟也來了鱼冀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤悠就,失蹤者是張志新(化名)和其女友劉穎千绪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梗脾,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡荸型,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炸茧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑞妇。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖梭冠,靈堂內(nèi)的尸體忽然破棺而出辕狰,到底是詐尸還是另有隱情,我是刑警寧澤控漠,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布蔓倍,位于F島的核電站,受9級特大地震影響盐捷,放射性物質(zhì)發(fā)生泄漏偶翅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一毙驯、第九天 我趴在偏房一處隱蔽的房頂上張望倒堕。 院中可真熱鬧,春花似錦爆价、人聲如沸垦巴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骤宣。三九已至秦爆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憔披,已是汗流浹背等限。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芬膝,地道東北人望门。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像锰霜,于是被迫代替她去往敵國和親筹误。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

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