Flask 学习-37.Flask-RESTful 序列化输出fields 字段设置

2022-09-06 14:17:51 浏览数 (1)

前言

前面一篇使用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 822ISO 8601

代码语言:javascript复制
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

报名费:报名费3000一人(周期3个月)

联系微信/QQ:283340479

0 人点赞