Это можно сделать двумя способами: предварительная и постсортировочная модификация. Предварительная сортировка удаляет все значения по умолчанию, присвоенные именам полей в client_fields dict
, но после сортировки сохраняет их.
В методе предварительной сортировки вам необходимо передать измененные поля с dict
по marshal
, если адрес электронной почты клиента None
.
Например;
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', '[email protected]')]
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": "[email protected]", "id": "2", "name": "John"}]
При постсортировке вы должны удалить поле электронной почты OrderedDict
, возвращенное marshal_with
, если оно None
.
Функция de_none
по умолчанию удаляет все поля, которые являются None
, или вы должны явно передать имена полей, если это нежелательно и вы также должны передать аргумент envelope
, если marshal_with
принимает то же самое.
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(default='[email protected]')
'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'), Client(2, 'john', '[email protected]')], 200, {'Etag': 'blah'}
#return [Client(1, 'Tom'), Client(2, 'john', '[email protected]')]
#return Client(1, 'Tom'), 200, {'Etag': 'foo'}
return Client(1, 'Tom')
print(json.dumps(get()))
@de_none()
@marshal_with(client_fields)
def get():
return Client(2, 'John', '[email protected]'), 201, {'Etag': 'ok'}
print(json.dumps(get()))
Выход;
{"id": "1", "name": "Tom"}
{"email": "[email protected]", "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', '[email protected]')]
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": "[email protected]",
"name": "John",
"id": "2"
}
]
обновление исправление flask_restful.marshal
Я отфильтровываю значения None
в genexp внутри функции marshal
и заменяю 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())
#filtering None
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', '[email protected]')]
return clients, 200
api.add_resource(ClientList, '/')
if __name__ == '__main__':
app.run(debug=True)
Выход;
[
{
"id": "1",
"name": "Tom"
},
{
"email": "[email protected]",
"id": "2",
"name": "John"
}
]
person
Nizam Mohamed
schedule
21.03.2016