1

I am building an api with flask_restplus. How the user gets/gives the data, is different of how is defined in my Models. This was easily solved with response marshalling https://flask-restplus.readthedocs.io/en/stable/marshalling.html#renaming-attributes

As I see it, the decorator @marshal_with() marshals the output response, but not the input (at least if the input and output models are different).

I was able to "rename" the input given by the user in a get calls by using the parser = reqparse.RequestParser() and parser.add_argument(), because the parser.parse_args() also does the "renaming". The problem comes to the post calls:

For the post calls, I cannot use the reqparse because the swagger documentation does not look well (How to document the post body using flask-ReSTplus?). Therefore I use a @expect() with the model I expect, but the expect does not rename the input json (simply validates). So what I have to end up doing is creating an additional Model and use marshal_with(api.payload) before managing the data. I however find it quite dirty because I need 3 models for one post call: ModelResponse ModelExpected ModelRename

Example

Let me put it in an example: in the internal system I call the fields as user_name and user_age but the client gets/gives the fields as name and age, then I have to:

# define the response model (includes the user id)
UserResponse = api.model("userResponse",
                           {
                            "name": fields.String(description="name of the user", attribute="user_name", required=True),
                            "age": fields.String(description="age of the user", attribute="user_age"),
                            "user_id": fields.String(description="User ID", required=True, attribute="user_id")
                           }

# define the expected model (to validate the input)
UserExpectPost = api.model("userExpectPost",
                             {
                               "name": fields.String(description="name of the user", attribute="user_name", required=True),
                               "age": fields.String(description="age of the user", required=True, attribute="user_age")
                             }
# define the model again, reverse from UserExpectPost (to rename the input attributes)
UserPostRename = api.model("userPostRename",
                             {
                               "user_name": fields.String(description="name of the user", attribute="name", required=True),
                               "user_age": fields.String(description="age of the user", required=True, attribute="age")
                             }


class Users(Resource):
   @api.marshal_with(UserResponse)
   @api.expect(UserExpectPost)
   def post(self):
      data = marshal(api.payload, UserPostRename)
      # do stuff 
      return # return whatever will be marshalled with UserResponse

Question

So, the question is, is there a way that I can do it with just 2 models? (just responseModel and inputModel). I would need something like the expect() but that also renames the attributes from the payload.

Dovi
  • 698
  • 9
  • 21
  • don't use this `data = marshal(api.payload, UserPostRename)`, it converts what is in your payload into what response should look like. for data use `data = api.payload` – waynetech May 20 '20 at 14:48
  • 1
    I need api.payload to be marshaled as UserPostRename model.. that's why I am marshaling. The response is marshaled with UserResponse model... – Dovi May 25 '20 at 16:20

0 Answers0