12.發佈問答界面設計及before_request鉤子函數

咱們如今開始設計發佈問答的界面與功能:css

clipboard.png

如今點擊它仍是沒有反應的,咱們要設計一個問答界面的html,而後把發佈問答連接過去。
首先編寫一個視圖函數以下:html

@app.route('/question/')
def question():
    return render_template('question.html')

base.html中爲發佈問答添加連接:數據庫

<li><a href="{{ url_for('question') }}">發佈問答</a></li>

問答頁面的html首先也要繼承導航條,而後主要界面設計以下:session

clipboard.png

這裏仍是利用cssBootstrap樣式完成的,問題描述區域是一個textarea,就不演示代碼了,我我的的經驗是,對於html的排版,要理解盒子模型,藉助border來查看元素的邊框,去看看marginpadding的寬度,排版好了以後把邊框去掉。
接下來是內容的提交,爲視圖函數提交POST方法,並把提交的內容寫入數據庫,修改question視圖函數,以下:app

@app.route('/question/', methods=['GET', 'POST'])
def question():
    if request.method == 'GET':
        return render_template('question.html')
    else:
        question_title = request.form.get('question_title')
        question_desc = request.form.get('question_desc')
        author_id = Users.query.filter(Users.username == session.get('username')).first().id
        new_question = Questions(title=question_title, content=question_desc, author_id=author_id)
        db.session.add(new_question)
        db.session.commit()
        return redirect(url_for('home'))

這裏其實與用戶註冊的原理是一致的,此時已經能把填寫的問題內容保存到數據庫了。須要注意的就是,新建一個question對象時,不只須要titlecontent,還須要帶上question對應的author_id,由於當初建立模型時這就是個非空字段。
代碼中咱們直接用session中的usernameUsers模型中檢索,來獲得author_id,看着很麻煩,並且在不少視圖函數中,咱們都須要用到當前登陸用戶的信息,所以可使用@app.before_request這個鉤子函數,看其名字就很好理解,是在request以前會自動運行的,咱們在每次請求以前(或者說每次運行視圖函數以前),都經過鉤子函數來獲得當期登陸用戶的User對象(而不是僅僅是session中的username),而後在須要的地方使用它,代碼以下:框架

@app.before_request
def my_before_request():
    username = session.get('username')
    if username:
        g.user = Users.query.filter(Users.username == username).first()

這個鉤子函數,從session中獲取當前登錄的username,若是獲取到了,再去檢索Users模型,把返回的user對象存入到g對象中,在視圖函數中咱們就能夠直接使用這個user對象的id/register_time等字段了。此時前面的視圖函數中的函數

author_id = Users.query.filter(Users.username == session.get('username')).first().id

能夠修改爲url

author_id = g.user.id

此外,發佈問題也須要用戶先登陸才能夠,若是用戶未登陸,@app.before_request沒法獲取到session中的username,此時g對象就沒有user這個屬性,所以咱們再次把question視圖修改以下:spa

@app.route('/question/', methods=['GET', 'POST'])
def question():
    if request.method == 'GET':
        return render_template('question.html')
    else:
        if hasattr(g, 'user'):
            question_title = request.form.get('question_title')
            question_desc = request.form.get('question_desc')
            author_id = g.user.id
            new_question = Questions(title=question_title, content=question_desc, author_id=author_id)
            db.session.add(new_question)
            db.session.commit()
            return redirect(url_for('home'))
        else:
            flash('請先登陸')
            return redirect(url_for('login'))

若是提交的時候未登陸,則跳轉到登錄頁面,而且flash'請先登陸'的提示。其實也能夠直接在get方法的時候就直接跳轉,避免用戶寫完了內容,又發現未登陸,頁面跳轉致使內容丟失。咱們先把框架搭起來,之後再逐步完善細節。設計


再注意到咱們以前寫的@app.context_processor上下文管理器,也是從session中取對象的,此時咱們能夠直接借用鉤子函數中的數據裏,所以將其改寫以下:

@app.context_processor
def my_context_processor():
    if hasattr(g, 'user'):
        return {'login_user': g.user}
    return {}

咱們以前說過由於g對象不能跨請求使用,所以在上下文管理器中用的是session,爲何這裏又用了g對象呢?緣由是如今有了鉤子函數,每次請求都會執行鉤子函數,向g對象中寫入user,因此上下文管理器一直都能從g對象中取到user,無論這個g對象是屬於哪次請求的。

相關文章
相關標籤/搜索