restful query python json python-2.7 null flask-restful

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" } ]