biosustain / potion

Flask-Potion is a RESTful API framework for Flask and SQLAlchemy, Peewee or MongoEngine
http://potion.readthedocs.io
Other
488 stars 51 forks source link

Copy ModelResource schema to route request schema with extra fields #174

Open bolerodan opened 5 years ago

bolerodan commented 5 years ago

I'm wondering if there is a way, to copy the schema from a ModelResource (Inline('self)) but add some extra fields onto it for another defined Route within that Resource, specifically the request_schema for said route.

The reasoning behind this, is that I have a route that requires the exact same properties as say, the create route, however I have a few extra ones required. This would just save having to duplicate writing out a fieldSet with all the properties from that model, just to include an extra one, and I dont want to include these extra fields on the Schema of the resource, as these are only related to this specific route and not the model.

Some way to do something even like

def create(self, properties: FieldSet({"extract": Inline('self'), "taxonomy_id": fields.Integer(nullable=True)}) )
lyschoening commented 5 years ago

Does the example you give below not work? You would want to give the route another name of course.

bolerodan commented 5 years ago

Does the example you give below not work? You would want to give the route another name of course.

EDIT: So it turns out I needed to set patchable=True on Inline and I had to use fields.Object, so this seems to be working now.

schema=fields.Object({"extract": Inline('self',patchable=Trye), "taxonomy_id": fields.Integer(nullable=True)})

Okay so, here is an example schema output for this route

    {
      "href": "/api/extract/tax",
      "method": "POST",
      "rel": "create_tax",
      "schema": {
        "additionalProperties": false,
        "properties": {
          "extract": {
            "$ref": "#"
          },
          "taxonomy_id": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },

here is the defined request schema

@Route.POST('/tax', rel="create_tax", schema=FieldSet({"extract": Inline('self'), "taxonomy_id": fields.Integer(nullable=True)}), response_schema=fields.List(Inline('self')))

I've also tried using just fields.Object

fields.Object({"extract": Inline("self"), "taxonomy_id": fields.Integer(nullable=True)})

When I submit this payload as an example

{
  "extract": {"produced_date": "2019-02-02"},
  "taxonomy_id": 1
}

I get this error response

{
  "errors": [
    {
      "message": "Additional properties are not allowed ('produced_date' was unexpected)",
      "path": [
        "extract"
      ],
      "validationOf": {
        "additionalProperties": false
      }
    }
  ],
  "message": "Bad Request",
  "status": 400
}

if I send an empty object as the extract, for example

{
  "extract": {},
  "taxonomy_id": 1
}

It submits fine, and if you print the passed argument, you see "taxonomy_id" 1 and a fully filled in object that matches inline(self) but all values set to None.

{'extract': {'produced_date': None, 'organization_id': None, 'researcher_id': None, 'extract_study_id': None, 'extract_medium_id': None, 'extract_extraction_method_id': None, 'extract_class_id': None, 'experiment_class_id': None, 'olive_id': None, 'fermentation_volume': None, 'fermentation_temperature': None, 'study_time': None, 'type': None, 'comments': None}, 'taxonomy_id': 1}

but produced_date is definitely a property on that model., it's also defined under schema

    class Schema:
        produced_date = fields.DateString(nullable=True)
        created_date = fields.DateTimeString(io="r")

You can post to its create route just fine with using produced_date. I must be missing something here. I appreciate your time!

lyschoening commented 5 years ago
if I send an empty object as the extract, for example

{ "extract": {}, "taxonomy_id": 1 }

It submits fine, and if you print the passed argument, you see "taxonomy_id" 1 and a fully filled in object that matches inline(self) but all values set to None.

{'extract': {'produced_date': None, 'organization_id': None, 'researcher_id': None, 'extract_study_id': None, 'extract_medium_id': None, 'extract_extraction_method_id': None, 'extract_class_id': None, 'experiment_class_id': None, 'olive_id': None, 'fermentation_volume': None, 'fermentation_temperature': None, 'study_time': None, 'type': None, 'comments': None}, 'taxonomy_id': 1}

but `produced_date` is definitely a property on that model., it's also defined under schema
class Schema:
    produced_date = fields.DateString(nullable=True)
    created_date = fields.DateTimeString(io="r")

You can post to its `create` route just fine with using `produced_date`. I must be missing something here. I appreciate your time!

That would be the expected input. You are posting an empty object and getting an empty resolved object as an argument. What input are you looking for in this situation?

bolerodan commented 5 years ago

That was just an example of me debugging the situation, not what I wanted.

I want to use Inline('self')

like the following

fields.Object({"extract": Inline("self"), "taxonomy_id": fields.Integer(nullable=True)})

However, when I send the following payload.

{
  "extract": {"produced_date": "2019-02-02"},
  "taxonomy_id": 1
}

"Additional properties are not allowed ('produced_date' was unexpected)" however thats misleading to me, produced_date is defined on the schema and on the model. But I'm only sending specific properties. Thats where I read up on the patch attribute for inline, which seems to allow you to patch specific properties on that schema.

It works now, so I suppose this can be closed. The original error message was confusing to me and I was unaware of the patch attribute on inline