码迷,mamicode.com
首页 > 其他好文 > 详细

Flask学习之九——用户信息界面

时间:2015-05-24 18:40:36      阅读:1533      评论:0      收藏:0      [点我收藏+]

标签:

1. 数据库中添加用户信息

app/models.py: 添加用户资料列

class User(UserMixin, db.Model):
    #...
    name = db.Column(db.String(64))
    location = db.Column(db.String(64))
    about_me = db.Column(db.Text())
    member_since = db.Column(db.DateTime(), default=datatime.utcnow)
    last_seen = db.Column(db.DateTime(), default=datatime.utcnow)

datetime.utcnow 后面没有 (),因为 db.Column()的 default 参数可以接受函数作为默认值,所以每次需要生成默认值时,db.Column() 都会调用指定的函数。

member_since字段创建时的初始值是当前时间,只需使用默认值即可,
last_seen 字段创建时的初始值也是当前时间,但用户每次访问网站后,这个值都会被刷新。可以在User模型中添加一个方法完成这个操作。

app/models.py: 刷新用户的最后访问时间

class User(UserMixin, db.Model):
    #...
    def ping(self):
        self.last_seen = datatime.utcnow()
        db.session.add(self)

每次收到用户的请求时都要调用该ping()方法。由于 auth 蓝本中的 before_app_request 处理程序会在每次请求前运行,所以能很轻松地实现这个需求。

app/auth/views.py: 更新已登录用户的访问时间

@auth.before_app_request
def before_request():
    if current_user.is_authenticated():
        current_user.ping()
        #...

 

 

 

 2. 用户信息页面

app/main/views.py: 信息页面的路由

@main.route(/user/<username>)
def user(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        abort(404)
    return render_template(user.html, user=user)

app/templates/user.html:用户信息页面的模板

{% block page_content %}
<div class="page-header">
    <h1>{{ user.username }}</h1>
    {% if user.name or user.location %}
    <p>
        {% if user.name %}{{ user.name }}{% endif %}
        {% if user.location %}
            From <a href="http://maps.google.com/?q={{ user.location }}">
                {{ user.location }}
            </a>
        {% endif %}
    </p>
    {% endif %}
    {% if current_user.is_administrator() %}
    <p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
    {% endif %}
    {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
    <p>
        Member since {{ moment(user.member_since).format(‘L‘) }}.
        Last seen {{ moment(user.last_seen).fromNow() }}.
    </p>
</div>
{% endblock %}

location 字段被渲染成指向谷歌地图的查询链接。
如果登录用户是管理员,那么就显示用户的电子邮件地址,且渲染成 mailto 链接。

在导航条中添加一个连接到用户资料界面的链接

app/templates/base.html

{% if current_user.is_authenticated() %}
<li>
    <a href="{{ url_for(‘main.user‘, username=current_user.username) }}">
        Profile
    </a>
</li>
{% endif %}

通过判断is_authenticated(), 未登录用户看不到那个按钮

 

 

 

3. 编辑页面

3.1 普通用户的编辑页面

app/main/forms.py: 编辑表单

class EditProfileForm(Form):
    name = StringField(Name, validators=[Length(0, 64)])
    location = StringField(Location, validators=[Length(0, 64)])
    about_me = TextAreaField(About Me)
    submit = SubmitField(Submit)

app/main/views.py: 资料编辑路由

@main.route(/edit-profile, methods=[GET, POST])
@login_required
def edit_profile():
    form = EditProfileForm()
    if form.validate_on_submit():
        current_user.name = form.name.data
        current_user.location = form.location.data
        current_user.about_me = form.about_me.data
        db.session.add(current_user)
        flash(Your profile has been updated.)
        return redirect(url_for(.user, username=current_user.username))
    form.name.data = current_user.name
    form.location.data = current_user.location
    form.about_me.data = current_user.about_me
    return render_template(edit_profile.html, form=form)

 

3.2 管理员的编辑页面

管理员使用的页面编辑表单比普通用户的表单更加复杂。除了前面的 3 个资料信息字段之外,管理员在表单中还要能编辑用户的电子邮件、用户名、确认状态和角色。

app/main/forms.py: 管理员编辑表单

class EditProfileAdminForm(Form):
    email = StringField(Email, validators=[Required(), Length(1, 64), Email()])
    username = StringField(Username, validators=[
                    Required(), Length(1, 64), Regexp(^[A-Za-z][A-Za-z0-9_.]*$, 0,
                                                    Usernames must have only letters, 
                                                    numbers, dots or underscores)])
    confirmed = BooleanField(Confirmed)
    role = SelectField(Role, coerce=int)
    name = StringField(Real name, validators=[Length(0, 64)])
    location = StringField(Location, validators=[Length(0, 64)])
    about_me = TextAreaField(About me)
    submit = SubmitField(Submit)

    def __init__(self, user, *args, **kwargs):
        super(EditProfileAdminForm, self).__init__(*args, **kwargs)
        self.role.choices = [(role.id, role.name) for role in Role.query.order_by(Role.name).all()]
        self.user = user

    def validate_email(self, field):
        if field.data != self.user.email and User.query.filter_by(email=field.data).first():
            raise ValidationError(Email already registered.)

    def validate_username(self, field):
        if field.data != self.user.username and User.query.filter_by(username=field.data).first():
            raise ValidationError(Username already in use.)

WTForms 对 HTML 表单控件 <select> 进行 SelectField 包装,从而实现下拉列表,用来在这个表单中选择用户角色。
SelectField 实例必须在其 choices 属性中设置各选项。选项必须是一个由元组组成的列表,各元组都包含两个元素:选项的标识符和显示在控件中的文本字符串。
choices 列表在表单的构造函数中设定,其值从 Role 模型中获取,使用一个查询按照角色名的字母顺序排列所有角色。
元组中的标识符是角色的 id,因为这是个整数,所以在 SelectField 构造函数中添加 coerce=int 参数,从而把字段的值转换为整数,而不使用默认的字符串。

app/main/views.py: 管理员编辑页面路由

main.route(/edit-profile/<int:id>, methods=[GET, POST])
@login_required
@admin_required
def edit_profile_admin(id):
    user = User.query.get_or_404(id)
    form = EditProfileAdminForm(user=user)
    if form.validate_on_submit():
        user.email = form.email.data
        user.username = form.username.data
        user.confirmed = form.confirmed.data
        user.role = Role.query.get(form.role.data)
        user.name = form.name.data
        user.location = form.location.data
        user.about_me = form.about_me.data
        db.session.add(user)
        flash(The profile has been updated.)
        return redirect(url_for(.user, username=user.username))
    form.email.data = user.email
    form.username.data = user.username
    form.confirmed.data = user.confirmed
    form.role.data = user.role_id
    form.name.data = user.name
    form.location.data = user.location
    form.about_me.data = user.about_me
    return render_template(edit_profile.html, form=form, user=user)

再探讨一下用于选择用户角色的 SelectField。
设定这个字段的初始值时,role_id 被赋值给了 field.role.data,这么做的原因在于 choices 属性中设置的元组列表使用数字标识符表示各选项。
表单提交后,id 从字段的 data 属性中提取,并且查询时会使用提取出来的 id 值加载角色对象。
表单中声明 SelectField 时使用 coerce=int 参数,其作用是保证这个字段的 data 属性值是整数。

 

 

 

 2015-05-24

Flask学习之九——用户信息界面

标签:

原文地址:http://www.cnblogs.com/whuyt/p/4526124.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!