文档章节

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

满满李
 满满李
发布于 2016/06/29 18:41
字数 2023
阅读 280
收藏 2

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

用户级别

FlaskWeb/app/auth/forms.py

#!/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为定义的资料编辑表单,由于这个表单中的所有的字段都是可选的,因此长度验证函数允许长度为0

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 .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完成赋值,当提交表单时,更新查出来的用户对象信息然后在把对象加到数据库中提交.

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

FlaskWeb/app/main/forms.py

#!/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,从而把字段的值转换为整数而不使用默认的字符串

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 -%}

说明:对于不同的角色用户个人设置页面可能不同,如上通过判断是否是管理员来访问不同的用户信息自定义页面

© 著作权归作者所有

满满李
粉丝 20
博文 156
码字总数 101605
作品 0
杭州
运维
私信 提问
981764793/PyFly

PyFly 项目介绍 Flask + Layui Fly Template实现的一个社区项目,使用flask-admin实现了简单的后台管理功能,数据库使用Mongodb,前台实现功能:用户注册、登录、邮件激活、发帖、回帖、点赞...

981764793
2018/04/28
0
0
《Flask Web开发:基于Python的Web应用开发实战》.PDF

简介 本书不仅适合初级Web开发人员学习阅读,更是Python程序员用来学习高级Web开发技术的优秀参考书。 • 学习Flask应用的基本结构,编写示例应用; • 使用必备的组件,包括模板、数据库、W...

jackmk
2017/08/12
0
0
网站后端.Flask.实战-社交博客开发-认证实现?

1.flask-login扩展负责管理已登录用户的会话 2.Werkzeug负责计算密码散列值并进行核对 3.itsdangerous生成并核对指定时间有效的加密安全token 说明:要实现对用户跟踪,必须让程序在用户登录后...

满满李
2016/06/25
74
0
Python学习资料篇

Python学习资料网络上比较多,看到好的资源,不及时记录下来,下次就找不到了,我把知乎上好的回到收藏汇总到这里,以便自己随时查看,包括Python入门书籍,网站,项目,官方文档,下载资源等...

BjarneCpp
2017/12/03
0
0
网站后端.Flask.实战-社交博客开发-flask-login认证用户?

用户模型 1.用户登录后认证状态需要被记录,这样浏览不同的页面才能记住这个状态,flask-login专门用来管理用户认证系统中的认证状态,且不依赖特定的认证机制 2.用户模型必须实现isauthenticat...

满满李
2016/06/26
590
0

没有更多内容

加载失败,请刷新页面

加载更多

SDKMAN推荐一个好

是在大多数基于Unix的系统上管理多个软件开发工具包的并行版本的工具。它提供了一个方便的命令行界面(CLI)和API来安装,切换,删除和列出sdk相关信息。以下是一些特性: By Developers, fo...

hotsmile
32分钟前
8
0
什么是 HDFS

是什么? HDFS 是基于 Java 的分布式文件系统,允许您在 Hadoop 集群中的多个节点上存储大量数据。 起源: 单机容量往往无法存储大量数据,需要跨机器存储。统一管理分布在集群上的文件系统称...

Garphy
35分钟前
4
0
一起来学Java8(四)——复合Lambda

在一起来学Java8(二)——Lambda表达式中我们学习了Lambda表达式的基本用法,现在来了解下复合Lambda。 Lambda表达式的的书写离不开函数式接口,复合Lambda的意思是在使用Lambda表达式实现函...

猿敲月下码
今天
10
0
debian10使用putty配置交换机console口

前言:Linux的推广普及,需要配合解决实际应用方能有成效! 最近强迫自己用linux进行实际工作,过程很痛苦,还好通过网络一一解决,感谢各位无私网友博客的帮助! 系统:debian10 桌面:xfc...

W_Lu
今天
12
0
aelf Enterprise 0.8.0 beta有奖公测,“Bug奖金计划”重磅开启

2019年9月30日,aelf Enterprise 0.8.0 beta版正式发布。aelf Enterprise 0.8.0 beta是一个完备的区块链系统, 包含完备的区块链系统、开发套件、开发文档、以及配套的基础应用和基础服务。 ...

AELF开发者社区
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部