python - query - Flask-RESTful: no devuelve propiedad del objeto en lugar de devolver nulo
flask restful get parameters (2)
Digamos que tengo una tabla de clientes con los campos de identificación , nombre y correo electrónico . Un campo de correo electrónico es opcional .
El código se ve así:
client_fields = {
''id'' : fields.String,
''name'' : fields.String,
''email'' : fields.String
}
Y para mostrar:
class ClientList(Resource):
@marshal_with(client_fields)
def get(self):
return model.Client.query.all()
Cuando no se proporciona el correo electrónico, API devuelve JSON así:
{
"id": "1",
"name": "John Doe",
"email": null
}
Pero, en cambio, quiero que devuelva este objeto:
{
"id": "1",
"name": "John Doe"
}
Lo que básicamente significa que en lugar de una propiedad con valor nulo, quiero que no devuelva ninguna propiedad . ¿Hay alguna manera de lograr eso?
marshal_with
la función de marshal
lugar del marshal_with
decorador:
class ClientList(Resource):
def get(self):
clients = []
for client in model.Client.query.all():
if client.email:
clients.append(marshal(client_fields))
else:
clients.append(marshal(client_fields_no_email))
return clients
O mejor
class ClientList(Resource):
def get(self):
return [client_marshal(client) for client in model.Client.query.all()]
con
def client_marshal(client):
if client.email:
return {''id'' : fields.String,
''name'' : fields.String,
''email'' : fields.String}
else:
return {''id'' : fields.String,
''name'' : fields.String}
Hay dos maneras de hacerlo, una modificación previa a la clasificación y una clasificación posterior. El preordenamiento elimina los valores predeterminados que se asignan a los nombres de los campos en el cuadro de diálogo de los campos de clientes, pero los mantiene almacenados.
En el método previo a la clasificación, debe pasar una función dict
fields to dict
modificada si el correo electrónico del cliente es None
.
Por ejemplo;
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()))
Salida;
[{"id": "1", "name": "Tom"}, {"email": "[email protected]", "id": "2", "name": "John"}]
En la clasificación posterior debe eliminar el campo de correo electrónico del OrderedDict
devuelto por marshal_with
si es None
.
La función de_none
por defecto elimina todos los campos que son None
o tiene que pasar explícitamente los nombres de campo si eso no se desea y también tiene que pasar el argumento de envelope
si marshal_with
toma el mismo.
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()))
Salida;
{"id": "1", "name": "Tom"}
{"email": "[email protected]", "id": "2", "name": "John"}
UPDATE Request hooks
El decorador app.after_request
se puede usar para modificar el objeto de respuesta. Cualquier valor predeterminado dado a los campos se conserva.
El gancho de solicitud remove_none_fields
toma el parámetro fields que puede ser None
para eliminar todos los campos con None
value o una lista de nombres de campo para eliminar selectivamente.
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)
Salida;
[
{
"age": null,
"name": "Tom",
"id": "1"
},
{
"age": null,
"email": "[email protected]",
"name": "John",
"id": "2"
}
]
actualización de parches flask_restful.marshal
Filtrado None
valores en un genexp dentro de la función de marshal
y reemplazo flask_restful.marshal
con marshal
definido aquí.
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)
Salida;
[
{
"id": "1",
"name": "Tom"
},
{
"email": "[email protected]",
"id": "2",
"name": "John"
}
]