有两种方法可以实现,即预编排和后编排修改。 预编排会删除客户端字段dict
中字段名称的任何默认值,但后编排会保留它们。
在预编排方法中,如果客户端的电子邮件为None
,则必须将修改后的字段dict
传递给marshal
函数。
例如;
import json
from flask_restful import fields, marshal, marshal_with
class Client(object):
def __init__(self, id, name, email=None):
self.id = id
self.name = name
self.email = email
client_fields = {
'id': fields.String,
'name': fields.String,
'email': fields.String
}
def get():
clients =[Client(1, 'Tom'), Client(2, 'John', 'john@example.com')]
return [marshal(client, client_fields if client.email else {k: v for k, v in client_fields.items() if k != 'email'}) for client in clients]
print(json.dumps(get()))
输出;
[{"id": "1", "name": "Tom"}, {"email": "john@example.com", "id": "2", "name": "John"}]
在后序编组中,如果marshal_with
返回的OrderedDict
的电子邮件字段为None
,则必须将其删除。
默认情况下,de_none
函数会删除所有None
字段,如果不希望这样做,则必须显式传递字段名称,并且如果marshal_with
接受相同的参数,则还必须传递envelope
参数。
from functools import wraps
import json
from flask_restful import fields, marshal, marshal_with
client_fields = {
'id': fields.String,
'name': fields.String,
'email': fields.String
}
class Client(object):
def __init__(self, id, name, email=None):
self.id = id
self.name = name
self.email = email
def de_none(envelope=None, *fields):
def decorator(func):
def dict_remove(d):
if fields:
for field in fields:
if d[field] is None:
d.pop(field)
else:
for k, v in d.items():
if v is None:
d.pop(k)
@wraps(func)
def decorated(*args, **kwargs):
data = result = func(*args, **kwargs)
if isinstance(result, tuple):
data = result[0]
if envelope:
data = data[envelope]
if isinstance(data, (list, tuple)):
for d in data:
dict_remove(d)
else:
dict_remove(data)
return result
return decorated
return decorator
@de_none()
@marshal_with(client_fields)
def get():
return Client(1, 'Tom')
print(json.dumps(get()))
@de_none()
@marshal_with(client_fields)
def get():
return Client(2, 'John', 'john@example.com'), 201, {'Etag': 'ok'}
print(json.dumps(get()))
输出;
{"id": "1", "name": "Tom"}
{"email": "john@example.com", "id": "2", "name": "John"}
更新 请求钩子
可以使用app.after_request
修饰符来修改响应对象。任何给定字段的默认值都将被保留。
remove_none_fields
请求钩子需要fields参数,该参数可以是None
来移除所有具有None
值的字段,或者是一个字段名称列表,以选择性地移除。
import json
from flask import Flask, Response
from flask_restful import fields, marshal_with, Api, Resource
app = Flask(__name__)
api = Api(app)
class Client(object):
def __init__(self, id, name, email=None):
self.id = id
self.name = name
self.email = email
client_fields = {
'id': fields.String,
'name': fields.String,
'email': fields.String,
'age': fields.String
}
class ClientList(Resource):
@marshal_with(client_fields)
def get(self):
clients =[Client(1, 'Tom'), Client(2, 'John', 'john@example.com')]
return clients, 200
@app.after_request
def remove_none_fields(resp, fields=('email',)):
"""
removes all None fields
"""
if not 'application/json' in resp.content_type:
return resp
def dict_remove(d, fields):
if fields:
for field in fields:
if d[field] is None:
d.pop(field)
else:
for k, v in tuple(d.items()):
if v is None:
d.pop(k)
data = json.loads(resp.get_data())
if isinstance(data, list):
for obj in data:
dict_remove(obj, fields)
else:
dict_remove(data, fields)
resp.set_data(json.dumps(data, indent=1))
resp.content_length = resp.calculate_content_length()
return resp
api.add_resource(ClientList, '/')
if __name__ == '__main__':
app.run(debug=True)
输出;
[
{
"age": null,
"name": "Tom",
"id": "1"
},
{
"age": null,
"email": "john@example.com",
"name": "John",
"id": "2"
}
]
更新 打补丁 flask_restful.marshal
我在marshal
函数内使用genexp过滤出None
值,并将flask_restful.marshal
替换为此处定义的marshal
。
from collections import OrderedDict
from flask import Flask
import flask_restful
from flask_restful import fields, marshal_with, Api, Resource
app = Flask(__name__)
api = Api(app)
class Client(object):
def __init__(self, id, name, email=None):
self.id = id
self.name = name
self.email = email
client_fields = {
'id': fields.String,
'name': fields.String,
'email': fields.String,
}
def marshal(data, fields, envelope=None):
def make(cls):
if isinstance(cls, type):
return cls()
return cls
if isinstance(data, (list, tuple)):
return (OrderedDict([(envelope, [marshal(d, fields) for d in data])])
if envelope else [marshal(d, fields) for d in data])
items = ((k, marshal(data, v) if isinstance(v, dict)
else make(v).output(k, data))
for k, v in fields.items())
items = ((k,v) for k, v in items if v is not None)
return OrderedDict([(envelope, OrderedDict(items))]) if envelope else OrderedDict(items)
flask_restful.marshal = marshal
class ClientList(Resource):
@marshal_with(client_fields)
def get(self):
clients =[Client(1, 'Tom'), Client(2, 'John', 'john@example.com')]
return clients, 200
api.add_resource(ClientList, '/')
if __name__ == '__main__':
app.run(debug=True)
输出;
[
{
"id": "1",
"name": "Tom"
},
{
"email": "john@example.com",
"id": "2",
"name": "John"
}
]