網站後端.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 -%}

說明:對於不一樣的角色用戶我的設置頁面可能不一樣,如上經過判斷是不是管理員來訪問不一樣的用戶信息自定義頁面

相關文章
相關標籤/搜索