咱们如今开始设计发布问答的界面与功能: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
对象是属于哪次请求的。