「Flask」鱼书项目实战五

栏目: Python · 发布时间: 5年前

内容简介:上一篇写到构建完了三个模型,但是和这三个模型直接关联的就是用户系统,所以开始完善用户逻辑。在创建之前,需要解决一个历史遗留问题,在运行flask程序的时候会报错,因为在创建为了先显示出注册页面,先传入一个空值

上一篇写到构建完了三个模型,但是和这三个模型直接关联的就是用户系统,所以开始完善用户逻辑。

在创建之前,需要解决一个历史遗留问题,在运行flask程序的时候会报错,因为在创建 Base 的时候我们是不希望它成为一个数据表的,但是我没没有指定他是一个基类,所以他会说我们没有指定这个表的主键,解决方案

class Base(db.Model):
    # 创建一个基类
    __abstract__ = True
    create_time = Column('create_time', Integer)
    status = Column(SmallInteger, default=1)

为了先显示出注册页面,先传入一个空值

# web/auth.py

@web.route('/register/', methods=['GET', 'POST'])
def register():
    return render_template('auth/register.html', form={'data':{}})

这样访问 127.0.0.1:5000/register 就可以看到正常的注册页面了。

编写注册验证表单

from wtforms import Form, StringField, IntegerField, PasswordField
from wtforms.validators import Length, NumberRange, DataRequired, Email


class RegisterForm(Form):
    email = StringField(validators=[DataRequired(), Length(8, 128), Email(message='电子邮箱不符合规范')])
    password = PasswordField(validators=[DataRequired(message='密码不可以为空,请输入密码'), Length(6, 32)])
    nickname = StringField(validators=[DataRequired(), Length(2, 10, message='昵称长度在2到10之间')])

数据的赋值

我们可以这样将表单的数据赋值给一个变量

@web.route('/register/', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User()
        user.nickname = form.nickname.data
        user.email = form.email

但是这样写显然不是很优雅,并且数据足够多的时候代码冗长。所以采用以下方法

表单验证的结果数据,赋值到User模型里,可以在Base类里编写一个set_attrs函数,统一将属性拷贝赋值。动态赋值。

#models/base.py
def set_attrs(self, attrs_dict):
    for key, value in attrs_dict.items():
        #id不需要被赋值
        if hasattr(self, key) and key != 'id':
            setattr(self, key, value)

属性描述符实现getter与setter

password 加密并加入模型中。

#models/sql_user.py
from sqlalchemy import Column, Integer, Float, String, Boolean
from werkzeug.security import generate_password_hash

from app.models.base import Base, db


class User(Base):
    id = Column(Integer, primary_key=True)
    _password = Column('password',)
    nickname = Column(String(24), nullable=False)
    phone_number = Column(String(18), unique=True)
    email = Column(String(50), unique=True, nullable=False)
    confirmed = Column(Boolean, default=False)
    beans = Column(Float, default=0)
    send_counter = Column(Integer, default=0)
    receive_counter = Column(Integer, default=0)
    wx_open_id = Column(String(50))
    wx_name = Column(String(32))

    #属性的getter
    @property
    def password(self):
        return self._password

    #属性的setter
    @password.setter
    def password(self, raw):
        self._password = generate_password_hash(raw)

这样子做了之后,当我们写入password的时候,先调用 setter 来写入加密的 password 然后读取密码的时候读取的就是加密后的密码而不是直接以前端传入的明文形式显示

保存信息到数据库

数据库有关操作可以看我以前的文章

@web.route('/register/', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User()
        user.set_attrs(form.data)
        #添加信息
        db.session.add(user)
        #提交
        db.session.commit()
    return render_template('auth/register.html', form=form)

这次修改除了将数据库提交代码加上去,还修改了模板中 form 关键字参数,这样就可以实现提示错误信息(在模板文件中输出error),并且如果输入错误,页面中的表单不会清空内容,用户体验较友好。

自定义验证器

因为邮箱不能相同,所以需要判断, wtforms 自带的验证器显然不能满足需求,需要自定义验证器

#forms/auth.py
from wtforms import Form, StringField, IntegerField, PasswordField
from wtforms.validators import Length, NumberRange, DataRequired, Email, ValidationError

from app.models.sql_user import User


class RegisterForm(Form):
    email = StringField(validators=[DataRequired(), Length(8, 64), Email(message='电子邮箱不符合规范')])
    password = PasswordField(validators=[DataRequired(message='密码不可以为空,请输入密码'), Length(6, 32)])
    nickname = StringField(validators=[DataRequired(), Length(2, 10, message='昵称长度在2到10之间')])

    #field.data相当于form.email,存放表单的数据
    #validata_value wtform会自动根据value来进行表单验证,所以不需要在email中导入自定义验证器
    def validate_email(self, field):
        #通过一次查询来判断是否有同名邮箱,有则抛出异常
        #使用first的原因是,不管返回几条只要存在一条相同的,就抛出异常
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('电子邮件已被注册')

同样的 nickname 也不能相同,所以通用添加自定义验证器

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

注册成功后重定向到登陆界面

@web.route('/register/', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User()
        user.set_attrs(form.data)
        db.session.add(user)
        db.session.commit()
        #别忘了导入redirect
        redirect(url_for('web.login'))
    return render_template('auth/register.html', form=form)

编写 login

@web.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm(request.form)
    if request.method == 'POST' and form.validate():
        #具体业务逻辑先不编写
        pass
    return render_template('auth/login.html', form=form)

同样的这里需要 login 的验证器 LoginForm

forms 下的 auth.py 下创建验证器

#forms/auth.py
class LoginForm(Form):
    email = StringField(validators=[DataRequired(), Length(8, 64), Email(message='电子邮箱不符合规范')])
    password = PasswordField(validators=[DataRequired(message='密码不可以为空,请输入密码'), Length(6, 32)])

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

超级运营术

超级运营术

韩叙 / 中信出版社 / 2017-5

新产品上线,为什么仅仅500次转发能带来300个内测用户? 为什么每一次内容推送,都带来App的一次卸载高峰? 同类活动那么多,怎样做才能超越竞品,占据头条? 为什么有的文案像“小广告”,有的文案像贴心老友? 创业公司与大平台的玩法有何不同? …… 如何从“了解运营”到“精通运营”,可能是运营人*的困惑。《超级运营术》正是对这个问题的全面解答。韩叙总结10年运营......一起来看看 《超级运营术》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具