flask-restx
flask-restx copied to clipboard
Nested field as part of entity
Hi, folks
So, in DB I have some flatten entity which represents account. It looks like:
data = {
"id": 1,
"name": "name",
"is_active": True,
"status": "INITIALIZING",
"created_date": "2020-08-12 15:47:15",
"created_user_id": "user 0001",
"updated_date": "2020-08-12 16:47:15",
"updated_user_id": "user 0002",
}
What I need is to transform it in structure like that:
{
"id": 1,
"name": "name",
"isActive": true,
"status": "INITIALIZING",
"created": {"user_id": null},
"updated": {"user_id": null}
}
Here is my code:
from flask_restx import fields, marshal, Namespace
import json
api = Namespace("sub-account", description="Sub account related operations")
sub_account_created_model = api.model(
"SubAccountCreated", {"user_id": fields.String(attribute="created_user_id")}
)
sub_account_updated_model = api.model(
"SubAccountUpdated", {"user_id": fields.String(attribute="updated_user_id")}
)
sub_account_model = api.model(
"SubAccount",
{
"id": fields.Integer(),
"name": fields.String(),
"isActive": fields.Boolean(attribute="is_active"),
"status": fields.String(),
"created": fields.Nested(sub_account_created_model),
"updated": fields.Nested(sub_account_updated_model),
},
)
data = {
"id": 1,
"name": "name",
"is_active": True,
"status": "INITIALIZING",
"created_date": "2020-08-12 15:47:15",
"created_user_id": "user 0001",
"updated_date": "2020-08-12 16:47:15",
"updated_user_id": "user 0002",
}
print(json.dumps(marshal(data, sub_account_model)))
But for some reason it doesn't populate nested user_id fields and result looks like:
{"id": 1, "name": "name", "isActive": true, "status": "INITIALIZING", "created": {"user_id": null}, "updated": {"user_id": null}}
Can some one point me what I am missing? It works if I change Nested fields to simple dict but it breaks me swagger:
sub_account_model = api.model(
"SubAccount",
{
"id": fields.Integer(),
"name": fields.String(),
"isActive": fields.Boolean(attribute="is_active"),
"status": fields.String(),
"created": {"user_id": fields.String(attribute="created_user_id")},
"updated": {"user_id": fields.String(attribute="updated_user_id")},
},
)
Used version: flask-restx = "==0.2.0"
The Nested field only uses the value of the attribute and not the whole object when marshalling with the nested model.
Probably the easiest solution is to subclass the Nested field and replace the output function:
class NestedFields(Nested):
def __init__(self, model, **kwargs):
super().__init__(model=model, **kwargs)
def output(self, key, obj, ordered=False):
if obj is None:
if self.allow_null:
return None
elif self.default is not None:
return self.default
# directly marshal with obj instead of obj.key or obj.attribute
return marshal(obj, self.nested, skip_none=self.skip_none, ordered=ordered)
@buehlefs thank you for the solution. This worked for me.
Thanks for the solution. How about adding some parameters like from_root=True then it can marshal using whole obj..?
The
Nestedfield only uses the value of the attribute and not the whole object when marshalling with the nested model.Probably the easiest solution is to subclass the
Nestedfield and replace the output function:class NestedFields(Nested): def __init__(self, model, **kwargs): super().__init__(model=model, **kwargs) def output(self, key, obj, ordered=False): if obj is None: if self.allow_null: return None elif self.default is not None: return self.default # directly marshal with obj instead of obj.key or obj.attribute return marshal(obj, self.nested, skip_none=self.skip_none, ordered=ordered)
Thank you so much, this worked like a charm!
To the maintainers: is it in plan to implement this feature natively (maybe with a parameter, like @smlee729 said)?