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对象是属于哪次请求的。

相关文章
相关标签/搜索