网站后端.Flask.实战-社交博客开发-资料编辑?

1.用户资料编辑分两种状况,普通用户只能编辑本身的资料页面,编辑后显示在本身的资料页面,管理员能够编辑任意用户的资料,包括用户所属的角色,如让指定用户称为内容管理员javascript

用户级别

FlaskWeb/app/auth/forms.pyhtml

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from ..models import User
from flask_wtf import Form
from wtforms import ValidationError
from wtforms.validators import Length, DataRequired, Email, Regexp, EqualTo
from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField

class EditProfileForm(Form):
    realname = StringField(u'真实姓名', validators=[
        Length(0, 64, u'长度必须在0-64之间')
    ])
    location = StringField(u'地址信息', validators=[
        Length(0, 128, u'长度必须在0-128之间')
    ])
    about_me = TextAreaField(u'我的简介')
    submit = SubmitField(u'更新')

说明:EditProfileForm为定义的资料编辑表单,因为这个表单中的全部的字段都是可选的,所以长度验证函数容许长度为0html5

FlaskWeb/app/main/views.pyjava

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from .. import db
from . import main
from .forms import EditProfileForm
from ..models import Permission, User
from flask_login import login_required, current_user
from ..decorators import admin_required, permission_required
from flask import render_template, redirect, url_for, abort, flash

@main.route('/admin')
@login_required
# @permission_required(Permission.ADMINISTER_POWER)
@admin_required
def admin():
    return redirect(url_for('admin.index'))

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

@main.route('/edit-profile/<username>', methods=['GET', 'POST'])
@login_required
def edit_profile(username):
    user = User.query.filter_by(username=username).first()
    if not user:
        abort(404)
    form = EditProfileForm()
    if form.validate_on_submit():
        if form.realname.data.strip():
            user.realname = form.realname.data
        if form.location.data.strip():
            user.location = form.location.data
        if form.about_me.data.strip():
            user.about_me = form.about_me.data
        db.session.add(user)
        db.session.commit()
        flash(u'信息已更新完毕', 'success')
        return redirect(url_for('main.user', username=user.username))
    form.realname.data = current_user.realname
    form.location.data = current_user.location
    form.about_me.data = current_user.about_me
    return render_template('edit_profile.html', form=form)

@main.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index.html')

说明:当form.validate_on_submit()验证失败时,获取当前用户的属性经过form.<field-named>.data完成赋值,当提交表单时,更新查出来的用户对象信息而后在把对象加到数据库中提交.python

FlaskWeb/app/templates/base.htmlsql

{%- extends 'bootstrap/base.html' -%}
{%- import 'bootstrap/wtf.html' as wtf -%}
{%- import 'bootstrap/utils.html' as utils -%}
{%- import 'bootstrap/fixes.html' as fixes -%}
{%- block html_attribs -%}
    {{ super() }}
    lang="zh-cn"
{%- endblock -%}
{%- block meta -%}
    {{ super() }}
    charset="utf-8"
{%- endblock -%}
{%- block title -%}
    {{ title|default('Flasky', true) }}
{%- endblock -%}
{%- block head -%}
    {{ super() }}
    {{ fixes.ie8() }}
    <link rel="shortcut icon"
          type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
    <link rel="icon"
          type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
{%- endblock -%}
{%- block navbar -%}
    <div class="navbar navbar-inverse" role="navigation">
        <div class="container">
            <div class="navbar-header">
                {# 说明: 先不支持响应式 #}
                <a class="navbar-brand" href="/">Flasky</a>
            </div>
            <div>
                <ul class="nav navbar-nav navbar-left">
                    <li class="active"><a href="/">Home</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {%- if current_user.is_authenticated -%}
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ current_user.username }}<b class="caret"></b> </a>
                            <ul class="dropdown-menu">
                                <li><a href="{{ url_for('main.user', username=current_user.username) }}">我的信息</a></li>
                                <li><a href="{{ url_for('main.edit_profile', username=current_user.username) }}">我的设置</a></li>
                                <li><a href="{{ url_for('auth.logout') }}">退出登出</a></li>
                            </ul>
                        </li>
                    {%- else -%}
                        <li><a href="{{ url_for('auth.login') }}">登陆</a></li>
                        <li><a href="{{ url_for('auth.register') }}">注册</a></li>
                    {%- endif -%}
                </ul>
            </div>
        </div>
    </div>
{%- endblock -%}
{%- block content -%}
    <div class="container">
        {%- block page_content -%}{%- endblock -%}
    </div>
{%- endblock -%}
{%- block styles -%}
    {{ super() }}
{%- endblock -%}
{%- block scripts -%}
    <script type="text/javascript"
            src="{{ url_for('main.static',filename='js/fixes/respond.min.js') }}"></script>
    <script type="text/javascript"
            src="{{ url_for('main.static',filename='js/fixes/html5shiv.min.js') }}"></script>
    {{ super() }}
    {{ moment.include_moment(local_js=url_for('main.static', filename='js/moment-with-locales.min.js')) }}
{%- endblock -%}

说明:为了让用户能轻易找到编辑页面,能够在base.html基模版中添加一个编辑页面连接数据库

管理级别

1.管理员资料编辑表单比普通表单更加复杂,除了上面的字段外还要能编辑用户的电子邮件,用户名,确认状态和角色flask

FlaskWeb/app/main/forms.pybootstrap

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from flask_wtf import Form
from ..models import User, Role
from wtforms import ValidationError
from wtforms.validators import Length, DataRequired, Email, Regexp, EqualTo
from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField, SelectField

class EditProfileForm(Form):
    realname = StringField(u'真实姓名', validators=[
        Length(0, 64, u'长度必须在0-64之间')
    ])
    location = StringField(u'地址信息', validators=[
        Length(0, 128, u'长度必须在0-128之间')
    ])
    about_me = TextAreaField(u'我的简介')
    submit = SubmitField(u'更新')

class EditProfileAdminForm(Form):
    email = StringField(u'邮箱地址', validators=[
        DataRequired(u'请填写此字段'),
        Length(1, 64,  u'长度必须在1-64之'),
        Email(u'邮箱地址格式有误'),
    ])
    username = StringField(u'用户名', validators=[
        DataRequired(u'请填写此字段'),
        Length(6, 64, u'长度必须在6-64之间'),
        Regexp(r'^[a-zA-Z][a-zA-Z0-9_]*$', 0, u'用户名只能包含字母数字下划线,字母开头')
    ])
    is_confirmed = BooleanField(u'邮箱已确认?', default=False)
    role = SelectField(u'角色设置', coerce=int)

    realname = StringField(u'真实姓名', validators=[
        Length(0, 64, u'长度必须在0-64之间')
    ])
    location = StringField(u'地址信息', validators=[
        Length(0, 128, u'长度必须在0-128之间')
    ])
    about_me = TextAreaField(u'我的简介')
    submit = SubmitField(u'更新')

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

    def validate_email(self, field):
        user = User.query.filter_by(email=field.data).first()
        if user:
            raise ValidationError(u'邮箱地址已被注册')

    def validate_username(self, field):
        user = User.query.filter_by(username=field.data).first()
        if user:
            raise ValidationError(u'用户名已被注册')

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

FlaskWeb/app/main/views.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from .. import db
from . import main
from ..models import Permission, User, Role
from flask_login import login_required, current_user
from .forms import EditProfileForm, EditProfileAdminForm
from ..decorators import admin_required, permission_required
from flask import render_template, redirect, url_for, abort, flash

@main.route('/admin')
@login_required
# @permission_required(Permission.ADMINISTER_POWER)
@admin_required
def admin():
    return redirect(url_for('admin.index'))

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

@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()
    if form.validate_on_submit():
        user.email = form.email.data
        user.username = form.username
        user.is_confirmed = form.is_confirmed
        user.role = Role.query.get(form.role.data)
        user.realname = form.realname.data
        user.location = form.location.data
        user.about_me = form.about_me.data
        db.session.add(user)
        flash(u'用户信息已更新')
        return redirect(url_for('main.user', username=user.username))
    form.email.data = user.email
    form.username.data = user.username
    form.is_confirmed.data = user.is_confirmed
    form.role.data = user.role_id
    form.realname.data = user.realname
    form.location.data = user.location
    form.about_me.data = user.about_me
    return render_template('edit_profile.html', form=form, user=user)

@main.route('/edit-profile/<username>', methods=['GET', 'POST'])
@login_required
def edit_profile(username):
    user = User.query.filter_by(username=username).first()
    if not user:
        abort(404)
    form = EditProfileForm()
    if form.validate_on_submit():
        if form.realname.data.strip():
            user.realname = form.realname.data
        if form.location.data.strip():
            user.location = form.location.data
        if form.about_me.data.strip():
            user.about_me = form.about_me.data
        db.session.add(user)
        db.session.commit()
        flash(u'信息已更新完毕', 'success')
        return redirect(url_for('main.user', username=user.username))
    form.realname.data = current_user.realname
    form.location.data = current_user.location
    form.about_me.data = current_user.about_me
    return render_template('edit_profile.html', form=form)

@main.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index.html')

说明:此视图函数中,用户由主键id指定,因而使用flask-sqlchemy中的get_or_404()函数,若是提供的id不正确,则返回404错误,用于选择用户的SelectField,在设定这个字段初始值时,role_id被赋值给了field.role.data,这么作的缘由在于choices属性中设置的元组列表使用数字标识符标识各项,表单提交后,id从字段data属性中提取,而且查询时会使用提取出来的id值加载角色对象

FlaskWeb/app/templates/base.html

{%- extends 'bootstrap/base.html' -%}
{%- import 'bootstrap/wtf.html' as wtf -%}
{%- import 'bootstrap/utils.html' as utils -%}
{%- import 'bootstrap/fixes.html' as fixes -%}
{%- block html_attribs -%}
    {{ super() }}
    lang="zh-cn"
{%- endblock -%}
{%- block meta -%}
    {{ super() }}
    charset="utf-8"
{%- endblock -%}
{%- block title -%}
    {{ title|default('Flasky', true) }}
{%- endblock -%}
{%- block head -%}
    {{ super() }}
    {{ fixes.ie8() }}
    <link rel="shortcut icon"
          type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
    <link rel="icon"
          type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
{%- endblock -%}
{%- block navbar -%}
    <div class="navbar navbar-inverse" role="navigation">
        <div class="container">
            <div class="navbar-header">
                {# 说明: 先不支持响应式 #}
                <a class="navbar-brand" href="/">Flasky</a>
            </div>
            <div>
                <ul class="nav navbar-nav navbar-left">
                    <li class="active"><a href="/">Home</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {%- if current_user.is_authenticated -%}
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ current_user.username }}<b class="caret"></b> </a>
                            <ul class="dropdown-menu">
                                <li><a href="{{ url_for('main.user', username=current_user.username) }}">我的信息</a></li>
                                {%- if current_user.is_administrator() -%}
                                    <li><a href="{{ url_for('main.edit_profile_admin', id=current_user.id) }}">我的设置</a></li>
                                {%- else -%}
                                    <li><a href="{{ url_for('main.edit_profile', username=current_user.username) }}">我的设置</a></li>
                                {%- endif -%}
                                <li><a href="{{ url_for('auth.logout') }}">退出登出</a></li>
                            </ul>
                        </li>
                    {%- else -%}
                        <li><a href="{{ url_for('auth.login') }}">登陆</a></li>
                        <li><a href="{{ url_for('auth.register') }}">注册</a></li>
                    {%- endif -%}
                </ul>
            </div>
        </div>
    </div>
{%- endblock -%}
{%- block content -%}
    <div class="container">
        {%- block page_content -%}{%- endblock -%}
    </div>
{%- endblock -%}
{%- block styles -%}
    {{ super() }}
{%- endblock -%}
{%- block scripts -%}
    <script type="text/javascript"
            src="{{ url_for('main.static',filename='js/fixes/respond.min.js') }}"></script>
    <script type="text/javascript"
            src="{{ url_for('main.static',filename='js/fixes/html5shiv.min.js') }}"></script>
    {{ super() }}
    {{ moment.include_moment(local_js=url_for('main.static', filename='js/moment-with-locales.min.js')) }}
{%- endblock -%}

说明:对于不一样的角色用户我的设置页面可能不一样,如上经过判断是不是管理员来访问不一样的用户信息自定义页面

相关文章
相关标签/搜索