内容简介:上一篇写到构建完了三个模型,但是和这三个模型直接关联的就是用户系统,所以开始完善用户逻辑。在创建之前,需要解决一个历史遗留问题,在运行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)])
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 「Flask实战」鱼书项目实战一
- 「Flask实战」鱼书项目实战三
- 「Flask实战」鱼书项目实战四
- 「Flask实战」鱼书项目实战六
- 「Flask实战」flask鱼书项目实战二
- go语言实战教程:Redis实战项目应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。