咱們如今開始設計發佈問答的界面與功能:css
如今點擊它仍是沒有反應的,咱們要設計一個問答界面的html
,而後把發佈問答連接過去。
首先編寫一個視圖函數以下:html
@app.route('/question/') def question(): return render_template('question.html')
在base.html
中爲發佈問答添加連接:數據庫
<li><a href="{{ url_for('question') }}">發佈問答</a></li>
問答頁面的html
首先也要繼承導航條,而後主要界面設計以下:session
這裏仍是利用css
加Bootstrap
樣式完成的,問題描述區域是一個textarea
,就不演示代碼了,我我的的經驗是,對於html
的排版,要理解盒子模型,藉助border
來查看元素的邊框,去看看margin
和padding
的寬度,排版好了以後把邊框去掉。
接下來是內容的提交,爲視圖函數提交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
對象時,不只須要title
和content
,還須要帶上question
對應的author_id
,由於當初建立模型時這就是個非空字段。
代碼中咱們直接用session
中的username
去Users
模型中檢索,來獲得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
對象是屬於哪次請求的。