1.用户模型的建立html
2.Marshmallow模块python
3.反序列化阶段对数据进行校验json
咱们当前开发的项目属于社交类型项目,因此关于用户的信息和功能直接贯穿了整个项目。因此此处实现用户模块功能,咱们先把用户基本信息构建起来,并经过基本信息实现用户注册登陆相关功能,后面遇到业务再继续扩展。flask
cd application/apps
python ../../manage.py blue -n users
application/settings/dev.pyapi
# 注册蓝图 INSTALLED_APPS = [ "application.apps.home", "application.apps.users", ]
application/urls.py数组
from application.utils import include urlpatterns = [ include("","home.urls"), include("/users","users.urls"), # *** ]
application/model/utils.pyapp
from application import db from datetime import datetime class BaseModel(db.Model): """公共模型""" __abstract__ = True # 抽象模型 id = db.Column(db.Integer, primary_key=True, comment="主键ID") name = db.Column(db.String(255), default="", comment="名称/标题") is_deleted = db.Column(db.Boolean, default=False, comment="逻辑删除") orders = db.Column(db.Integer, default=0, comment="排序") status = db.Column(db.Boolean, default=True, comment="状态(是否显示,是否激活)") created_time = db.Column(db.DateTime, default=datetime.now, comment="建立时间") updated_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name)
application/apps/users/models.py
from application.utils.models import BaseModel,db from werkzeug.security import generate_password_hash, check_password_hash class User(BaseModel): """用户基本信息表""" __tablename__ = "mf_user" name = db.Column(db.String(255), index=True, comment="用户帐户") nickname = db.Column(db.String(255), comment="用户昵称") _password = db.Column(db.String(255), comment="登陆密码") age = db.Column(db.SmallInteger, comment="年龄") money = db.Column(db.Numeric(7,2), comment="帐户余额") ip_address = db.Column(db.String(255), default="", index=True, comment="登陆IP") intro = db.Column(db.String(500), default="", comment="个性签名") avatar = db.Column(db.String(255), default="", comment="头像url地址") sex = db.Column(db.SmallInteger, default=0, comment="性别" ) # 0表示未设置,保密, 1表示男,2表示女 email = db.Column(db.String(32), index=True, default="", nullable=False, comment="邮箱地址") mobile = db.Column(db.String(32), index=True, nullable=False, comment="手机号码") unique_id = db.Column(db.String(255), index=True, default="", comment="客户端惟一标记符") province = db.Column(db.String(255), default="", comment="省份") city = db.Column(db.String(255), default="", comment="城市") area = db.Column(db.String(255), default="", comment="地区") info = db.relationship('UserProfile', backref='user', uselist=False) @property def password(self): return self._password @password.setter def password(self, rawpwd): """密码加密""" self._password = generate_password_hash(rawpwd) def check_password(self, rawpwd): """验证密码""" return check_password_hash(self.password, rawpwd) class UserProfile(BaseModel): """用户详情信息表""" __tablename__ = "mf_user_profile" user_id = db.Column(db.Integer,db.ForeignKey('mf_user.id'), comment="用户ID") education = db.Column(db.Integer, comment="学历教育") middle_school = db.Column(db.String(255), default="", comment="初中/中专") high_school = db.Column(db.String(255), default="", comment="高中/高职") college_school = db.Column(db.String(255), default="", comment="大学/大专") profession_cate = db.Column(db.String(255), default="", comment="职业类型") profession_info = db.Column(db.String(255), default="", comment="职业名称") position = db.Column(db.SmallInteger, default=0, comment="职位/职称") emotion_status = db.Column(db.SmallInteger, default=0, comment="情感状态") birthday =db.Column(db.DateTime, default="", comment="生日") hometown_province = db.Column(db.String(255), default="", comment="家乡省份") hometown_city = db.Column(db.String(255), default="", comment="家乡城市") hometown_area = db.Column(db.String(255), default="", comment="家乡地区") hometown_address = db.Column(db.String(255), default="", comment="家乡地址") living_province = db.Column(db.String(255), default="", comment="现居住省份") living_city = db.Column(db.String(255), default="", comment="现居住城市") living_area = db.Column(db.String(255), default="", comment="现居住地区") living_address = db.Column(db.String(255), default="", comment="现居住地址")
执行数据库迁移命令
cd ../.. # 切换工做目录会到项目根目录,manage.py所在目录下 python manage.py db init python manage.py db migrate -m "users table" python manage.py db upgrade
在开发中,针对客户端提交的数据进行验证或提供模型数据转换格式成字典给客户端。可使用Marshmallow模块来进行。
下面咱们来了解一下Marshmallow模块.
Marshmallow,中文译做:棉花糖。是一个轻量级的数据格式转换的模块,也叫序列化和反序列化模块,经常使用于将复杂的orm模型对象与python原生数据类型之间相互转换。marshmallow提供了丰富的api功能。以下:
Serializing
序列化[能够把数据对象转化为可存储或可传输的数据类型,例如:objects/object->list/dict,dict/list->string]
Deserializing
反序列化器[把可存储或可传输的数据类型转换成数据对象,例如:list/dict->objects/object,string->dict/list]
Validation
数据校验,能够在反序列化阶段,针对要转换数据的内容进行类型验证或自定义验证。
Marshmallow自己是一个单独的库,基于咱们当前项目使用框架是flask而且数据库ORM框架使用SQLAlchemy,因此咱们能够经过安装flask-sqlalchemy和marshmallow-sqlalchemy集成到项目就能够了。
pip install -U marshmallow-sqlalchemy pip install -U flask-sqlalchemy pip install -U flask-marshmallow
import os from flask_marshmallow import Marshmallow ... # 数据转换器的对象建立 ma = Marshmallow() def init_app(config_path): ... # 数据转换器的初始化 ma.init_app(app)
为了方便学习和使用Marshllow, 咱们单首创建一个蓝图来验证这个模块的基本使用.
cd application/apps
python ../../manage.py blue -n marsh
INSTALLED_APPS = [ "application.apps.home", "application.apps.users", "application.apps.marsh", ]
from application.utils import include urlpatterns = [ include("","home.urls"), include("/users","users.urls"), include("/marsh","marsh.urls"), ]
marshmallow转换数据格式主要经过构造器类来完成,而Schema类提供了数据转换的基本功能:序列化,验证和反序列化。因此在使用marshmallow的过程当中全部的构造器类必须直接或间接继承于Schema基类
application/apps/marsh/urls.py
from . import views from application.utils import path urlpatterns = [ path("", views.index), ]
application/apps/marsh/views.py
from marshmallow import Schema,fields from application.apps.users.models import User,UserProfile class UserSchema(Schema): name = fields.String() age = fields.Integer() email = fields.Email() money = fields.Number() class Meta: fields = ["name","age","money","email","info"] ordered = True # 转换成有序字典 def index(): """序列化""" """单个模型数据的序列化处理""" user1 = User(name="xiaoming", password="123456", age=16, email="333@qq.com", money=31.50) data1 = UserSchema().dump(user1) # 将模型类对象序列化为字典dict格式 data2 = UserSchema().dumps(user1) # 把模型对象转换成json字符串格式 return "ok"
在前面进行的序列化操做属于序列化单个数据对象, MarshMallow中也能够进行多个数据对象的序列化.
application/apps/marsh/views.py
from marshmallow import Schema,fields from application.apps.users.models import User,UserProfile class UserSchema(Schema): name = fields.String() age = fields.Integer() email = fields.Email() money = fields.Number() class Meta: fields = ["name","age","money","email","info"] ordered = True # 转换成有序字典 def index(): """序列化""" """多个模型数据的序列化""" user1 = User(name="xiaoming", password="123456", age=15, email="333@qq.com", money=31.50) user2 = User(name="xiaohong", password="123456", age=16, email="333@qq.com", money=31.50) user3 = User(name="xiaopang", password="123456", age=17, email="333@qq.com", money=31.50) data_list = [user1,user2,user3] data1 = UserSchema(many=True).dumps(data_list) # 注意:序列化多个数据对象要加many=True return "ok"
application/apps/marsh/views.py
from marshmallow import Schema,fields from application.apps.users.models import User,UserProfile class UserProfileSchema(Schema): education = fields.Integer() middle_school = fields.String() class UserSchema(Schema): name = fields.String() age = fields.Integer() email = fields.Email() money = fields.Number() # only的含义是外层序列化器要内层序列化器的哪些字段 info = fields.Nested(UserProfileSchema,only=["middle_school"]) class Meta: fields = ["name","age","money","email","info"] ordered = True # 转换成有序字典 def index(): """序列化""" """序列化嵌套使用""" user1 = User(name="xiaoming", password="123456", age=15, email="333@qq.com", money=31.50) user1.info = UserProfile( education=3, middle_school="北京师范学院附属中学白沙路分校" ) data = UserSchema().dump(user1) data = UserSchema().dumps(user1) print(data) return "ok"
from marshmallow import Schema, fields, validate, ValidationError,post_load class UserSchema2(Schema): name = fields.String() sex = fields.String() age = fields.Integer(missing=18) # 反序列化数据时,若是数据没有给age字段赋值,则age默认值为18 email = fields.Email() mobile = fields.String() @post_load def post_load(self, data, **kwargs): return User(**data) def index(): user_data = {"mobile":"1331345635","name": "xiaoming", "email": "xiaoming@qq.com","sex":"abc"} us2 = UserSchema2() result = us2.load(user_data) # 将字典转化为模型类对象 print(type(result),result) # <class 'application.apps.users.models.User'> <User: xiaoming> return "ok"
将user_data数据反序列化后再序列化,能够看到结果多了age:18这一项,这就是由于在schema中的age字段中设置了missing=18
from marshmallow import Schema, fields, validate, ValidationError,post_load class UserSchema2(Schema): name = fields.String() sex = fields.String() age = fields.Integer(missing=18) email = fields.Email() # 设置反序列化时必需要有mobile字段 mobile = fields.String(required=True) @post_load def post_load(self, data, **kwargs): return User(**data) def index(): user_data = {"name": "xiaoming","sex":"abc"} us2 = UserSchema2() # 设置反序列化时能够忽略部分数据 result = us2.load(user_data,partial=True) print(result) # ==> <User xiaoming> return "ok"
from marshmallow import Schema, fields, validate, ValidationError,post_load class UserSchema2(Schema): name = fields.String() sex = fields.Integer() age = fields.Integer(missing=18) email = fields.Email() mobile = fields.String() password = fields.String(load_only=True) # 设置当前字段为只写字段,只会在反序列化阶段启用 @post_load def post_load(self, data, **kwargs): return User(**data) def index(): user_data = {"name": "xiaoming","password":"123456","sex":1} us2 = UserSchema2() # 反序列化 result = us2.load(user_data) print(result) # ==> <User xiaoming> # 序列化 us3 = UserSchema2(only=["sex","name","age"]) # 限制处理的字段,也就是序列化出来只有这三个字段 result2 = us3.dump(result) print(result2) return "ok" ''' class UserSchema(Schema): name = fields.Str() # password is password = fields.Str(load_only=True) # 至关于只写字段 "write-only" created_time = fields.DateTime(dump_only=True) # 至关于只读字段 "read-only" '''
post_dump([fn,pass_many,pass_original]) 注册序列化对象后调用的方法,它会在对象序列化后被调用。
post_load([fn,pass_many,pass_original]) 注册反序列化对象后要调用的方法,它会在验证数据以后被调用。
pre_dump([fn,pass_many]) 注册序列化对象以前调用的方法,它会在序列化对象以前被调用。
pre_load([fn,pass_many]) 在反序列化对象以前,注册要调用的方法,它会在验证数据以前调用。
from marshmallow import Schema, fields, validate, ValidationError,post_load,post_dump class UserSchema2(Schema): name = fields.String() sex = fields.Integer(validate=validate.OneOf([0,1,2])) age = fields.Integer(missing=18) email = fields.Email() mobile = fields.String() password = fields.String(load_only=True) # 设置当前字段为只写字段,只会在反序列化阶段启用 @post_load def post_load(self, data, **kwargs): return User(**data) @post_dump def post_dump(self,data, **kwargs): data["mobile"] = data["mobile"][:3] +"*****"+ data["mobile"][-3:] return data def index(): user_data = {"name": "xiaoming","password":"123456","sex":1,"mobile":"133123454656"} us2 = UserSchema2()
# 反序列化 result = us2.load(user_data) print(result) # ==> <User xiaoming>
# 序列化 us3 = UserSchema2(only=["sex","name","age","mobile"]) # 限制处理的字段 result2 = us3.dump(result) print(result2) return "ok"
描述 | |
---|---|
fields.Dict (keys, type]] = None, values, …) |
字典类型,经常使用于接收json类型数据 |
fields.List (cls_or_instance, type], **kwargs) |
列表类型,经常使用于接收数组数据 |
fields.Tuple (tuple_fields, *args, **kwargs) |
元组类型 |
fields.String (*, default, missing, data_key, …) |
字符串类型 |
fields.UUID (*, default, missing, data_key, …) |
UUID格式类型的字符串 |
fields.Number (*, as_string, **kwargs) |
数值基本类型 |
fields.Integer (*, strict, **kwargs) |
整型 |
fields.Decimal (places, rounding, *, allow_nan, …) |
数值型 |
fields.Boolean (*, truthy, falsy, **kwargs) |
布尔型 |
fields.Float (*, allow_nan, as_string, **kwargs) |
浮点数类型 |
fields.DateTime (format, **kwargs) |
日期时间类型 |
fields.Time (format, **kwargs) |
时间类型 |
fields.Date (format, **kwargs) |
日期类型 |
fields.Url (*, relative, schemes, Set[str]]] = None, …) |
url网址字符串类型 |
fields.Email (*args, **kwargs) |
邮箱字符串类型 |
fields.IP (*args[, exploded]) |
IP地址字符串类型 |
fields.IPv4 (*args[, exploded]) |
IPv4地址字符串类型 |
fields.IPv6 (*args[, exploded]) |
IPv6地址字符串类型 |
fields.Method (serialize, deserialize, **kwargs) |
基于Schema类方法返回值的字段 |
fields.Function (serialize, Any], Callable[[Any, …) |
基于函数返回值得字段 |
fields.Nested (nested, type, str, Callable[[], …) |
外键类型 |
描述 | |
---|---|
default | 序列化阶段中设置字段的默认值 |
missing | 反序列化阶段中设置字段的默认值 |
validate | 反序列化阶段调用的内置数据验证器或者内置验证集合 |
required | 设置当前字段的必填字段 |
allow_none | 是否容许为空 |
load_only | 是否在反序列化阶段才使用到当前字段 |
dump_omly | 是否在序列化阶段才使用到当前字段 |
error_messages | 字典类型,能够用来替代默认的字段异常提示语,格式:<br>error_messages={“required”: “用户名为必填项。”} |
描述 | |
---|---|
validate.Email (*, error) |
邮箱验证 |
validate.Equal (comparable, *, error) |
判断值是否相等 |
validate.Length (min, max, *, equal, error) |
值长度/大小验证 |
validate.OneOf (choices, labels, *, error) |
选项验证 |
validate.Range ([min, max]) |
范围验证 |
validate.Regexp (regex, bytes, Pattern][, flags]) |
正则验证 |
validate.URL (*, relative, schemes, Set[str]]] = None, …) |
验证是否为URL |
from marshmallow import Schema, fields, validate, ValidationError,post_load class UserSchema3(Schema): name = fields.String(required=True) sex = fields.String(required=True,error_messages={"required":"对不起,permission必须填写"}) age = fields.Integer(missing=18,validate=validate.Range(min=18,max=40,error="年龄必须在18-40之间!")) # 限制数值范围 email = fields.Email(error_messages={"invalid":"对不起,必须填写邮箱格式!"}) mobile = fields.String(required=True, validate=validate.Regexp("^1[3-9]\d{9}$",error="手机号码格式不正确"),error_messages={"Regexp":"手机格式不正确"}) @post_load def make_user_obj(self, data, **kwargs): return User(**data) def index3(): user_data = {"mobile":"1331345635","name": "xiaoming","age":40, "email": "xiaoming@qq.com","sex":"abc"} us2 = UserSchema3() result = us2.load(user_data) result2 = us2.dumps(result) print(result) print(result2) return "ok"
局部钩子和全局钩子,好比局部钩子对单个字段(用户名)的判断,以及全局钩子对密码,确认密码多个字段的判断
from marshmallow import Schema, fields, validate,validates, ValidationError,post_load,validates_schema class UserSchema4(Schema): name = fields.String(required=True) sex = fields.String(required=True,error_messages={"required":"对不起,permission必须填写"}) age = fields.Integer(missing=18,validate=validate.Range(min=18,max=40,error="年龄必须在18-40之间!")) # 限制数值范围 email = fields.Email(error_messages={"invalid":"对不起,必须填写邮箱格式!"}) mobile = fields.String(required=True, validate=validate.Regexp("^1[3-9]\d{9}$",error="手机号码格式不正确"),error_messages={"Regexp":"手机格式不正确"}) password = fields.String(required=True, load_only=True) password2 = fields.String(required=True, allow_none=True) @post_load def make_user_obj(self, data, **kwargs): return User(**data) # 局部钩子 *** @validates("name") def validate_name(self,data,**kwargs): print("name=%s" % data) if data == "root": raise ValidationError({"对不起,root用户是超级用户!您没有权限注册!"}) # 必须有返回值 return data # 全局钩子 *** @validates_schema def validate(self,data,**kwargs): print(data) if data["password"] != data["password2"]: raise ValidationError("密码和确认密码必须同样!") data.pop("password2") return data def index(): user_data = {"password":"12345","password2":"123456","mobile":"13313345635","name": "root1","age":40, "email": "xiaoming@qq.com","sex":"abc"} us2 = UserSchema4() result = us2.load(user_data) print(result) return "ok"
类型描述fields.Dict(keys, type]] = None, values, …)字典类型,经常使用于接收json类型数据fields.List(cls_or_instance, type], **kwargs)列表类型,经常使用于接收数组数据fields.Tuple(tuple_fields, *args, **kwargs)元组类型fields.String(*, default, missing, data_key, …)字符串类型fields.UUID(*, default, missing, data_key, …)UUID格式类型的字符串fields.Number(*, as_string, **kwargs)数值基本类型fields.Integer(*, strict, **kwargs)整型fields.Decimal(places, rounding, *, allow_nan, …)数值型fields.Boolean(*, truthy, falsy, **kwargs)布尔型fields.Float(*, allow_nan, as_string, **kwargs)浮点数类型fields.DateTime(format, **kwargs)日期时间类型fields.Time(format, **kwargs)时间类型fields.Date(format, **kwargs)日期类型fields.Url(*, relative, schemes, Set[str]]] = None, …)url网址字符串类型fields.Email(*args, **kwargs)邮箱字符串类型fields.IP(*args[, exploded])IP地址字符串类型fields.IPv4(*args[, exploded])IPv4地址字符串类型fields.IPv6(*args[, exploded])IPv6地址字符串类型fields.Method(serialize, deserialize, **kwargs)基于Schema类方法返回值的字段fields.Function(serialize, Any], Callable[[Any, …)基于函数返回值得字段fields.Nested(nested, type, str, Callable[[], …)外键类型