前言
前面一篇使用Flask-RESTful 已经实现查询对象的序列化输出成json,这篇继续讲下一些特殊字段的处理
模型
user 表结构设计
代码语言:javascript复制from . import db
from passlib.hash import sha256_crypt
from datetime import datetime
class Users(db.Model):
__tablename__ = 'user' # 数据库表名
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(128), nullable=False)
is_active = db.Column(db.Boolean, default=1)
email = db.Column(db.String(64), nullable=True)
create_time = db.Column(db.DateTime, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, default=datetime.now)
def hash_password(self, password):
"""密码加密"""
self.password = sha256_crypt.encrypt(password)
def verify_password(self, password):
"""校验密码"""
return sha256_crypt.verify(password, self.password)
def __repr__(self):
return f"<Users(id='{self.id}', username='{self.username}'...)>"
日期字段
create_time 和 update_time 是DateTime 时间类型
代码语言:javascript复制from flask_restful import Resource, fields, marshal_with, marshal
user_fields = {
'id': fields.Integer,
'username': fields.String,
'is_active': fields.Boolean,
'create_time': fields.DateTime,
'update_time': fields.DateTime,
}
class UserInfo(Resource):
def get(self):
user = Users.query.get(1)
print(f'查询到的数据:{user}')
print(user.update_time)
return {
"code": 0,
"msg": "success",
"data": marshal(user, user_fields)
}
序列化后看到的时间格式如下
代码语言:javascript复制{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"username": "test",
"is_active": true,
"create_time": "Fri, 02 Sep 2022 10:24:48 -0000",
"update_time": "Fri, 02 Sep 2022 10:24:51 -0000"
}
}
在官方文档中可以看到,DateTime类型可以支持2种时间格式 RFC 822
和 ISO 8601
user_fields = {
'id': fields.Integer,
'username': fields.String,
'is_active': fields.Boolean,
'create_time': fields.DateTime(dt_format='iso8601'),
'update_time': fields.DateTime(dt_format='rfc822')
}
接下来看下这2种日期格式的显示
代码语言:javascript复制HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Fri, 02 Sep 2022 02:39:01 GMT
Content-Type: application/json
Content-Length: 236
Connection: close
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"username": "test",
"is_active": true,
"create_time": "2022-09-02T10:24:48",
"update_time": "Fri, 02 Sep 2022 10:24:51 -0000"
}
}
很显然iso8601 格式的日期有个T,不是我们需要的。但是官方给的文档说只支持这2种格式,如果想输出自己想要的格式,就需要自定义字段了。
自定义字段和多个值
先看下官方文档给的示例: 有时您有自己的自定义格式需求。您可以子类化 fields.Raw该类并实现该format功能。这在属性存储多条信息时特别有用。 例如,一个位域,其各个位代表不同的值。您可以使用字段将单个属性多路复用到多个输出值。 此示例假定flags属性中的第 1 位表示“正常”或“紧急”项目,第 2 位表示“已读”或“未读”。 这些项目可能很容易存储在位域中,但对于人类可读的输出,最好将它们转换为单独的字符串字段。
自定义格式是使用 fields.Raw,继承 fields.Raw 然后实现 format 函数,官方的例子:
代码语言:javascript复制class UrgentItem(fields.Raw):
def format(self, value):
return "Urgent" if value & 0x01 else "Normal"
class UnreadItem(fields.Raw):
def format(self, value):
return "Unread" if value & 0x02 else "Read"
fields = {
'name': fields.String,
'priority': UrgentItem(attribute='flags'),
'status': UnreadItem(attribute='flags'),
}
接下来自定义 CustomDate, 需重写format方法
代码语言:javascript复制from flask_restful import fields
class CustomDate(fields.DateTime):
'''
自定义CustomDate,原有的fileds.DateTime序列化后
只支持 rfc822,ios8601 格式,新增 strftime 格式
strftime格式下支持 format 参数,默认为 '%Y-%m-%d %H:%M:%S'
'''
def __init__(self, dt_format='rfc822', format=None, **kwargs):
super().__init__(**kwargs)
self.dt_format = dt_format
def format(self, value):
if self.dt_format in ('rfc822', 'iso8601'):
return super().format(value)
elif self.dt_format == 'strftime':
if isinstance(value, str):
return value
return value.strftime('%Y-%m-%d %H:%M:%S')
else:
raise Exception('Unsupported date format %s' % self.dt_format)
使用示例
代码语言:javascript复制user_fields = {
'id': fields.Integer,
'username': fields.String,
'is_active': fields.Boolean,
'create_time': CustomDate(dt_format='iso8601'),
'update_time': CustomDate(dt_format='strftime')
}
重命名属性
通常,您面向公众的字段名称与您的内部字段名称不同。要配置此映射,请使用attribute关键字参数。
代码语言:javascript复制user_fields = {
'id': fields.Integer,
'name': fields.String(attribute='username'),
'is_active': fields.Boolean,
'create_time': CustomDate(dt_format='iso8601'),
'update_time': CustomDate(dt_format='strftime')
}
输出的 name 字段对应数据库的 username
代码语言:javascript复制{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"name": "test",
"is_active": true,
"create_time": "2022-09-02T10:24:48",
"update_time": "2022-09-02 10:24:51"
}
}
默认值
如果由于某种原因您的数据对象在字段列表中没有属性,您可以指定要返回的默认值而不是None.
代码语言:javascript复制user_fields = {
'id': fields.Integer,
'name': fields.String(attribute='username'),
'is_active': fields.Boolean,
'create_time': CustomDate(dt_format='iso8601'),
'update_time': CustomDate(dt_format='strftime'),
'address': fields.String(default='shang hai')
}
序列化后显示
代码语言:javascript复制{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"name": "test",
"is_active": true,
"create_time": "2022-09-02T10:24:48",
"update_time": "2022-09-02 10:24:51",
"address": "shang hai"
}
}
网址和其他具体字段
Flask-RESTful 包含一个特殊字段 ,fields.Url它为所请求的资源合成一个 uri。这也是如何将数据添加到您的响应中的一个很好的示例,这些数据实际上并不存在于您的数据对象中。:
代码语言:javascript复制class RandomNumber(fields.Raw):
def output(self, key, obj):
return random.random()
fields = {
'name': fields.String,
# todo_resource is the endpoint name when you called api.add_resource()
'uri': fields.Url('todo_resource'),
'random': RandomNumber,
}
默认情况下fields.Url返回一个相对 uri。要生成包含方案、主机名和端口的绝对 uri,请 absolute=True在字段声明中传递关键字参数。要覆盖默认方案,请传递scheme关键字参数:
代码语言:javascript复制fields = {
'uri': fields.Url('todo_resource', absolute=True),
'https_uri': fields.Url('todo_resource', absolute=True, scheme='https')
}
2022年第 12期《python接口web自动化 测试开发》课程,9月17号开学!
本期上课时间:2022年9月17号 - 2022年12月17号,周六周日上午9:00-11:00