marshmallow-code / webargs

A friendly library for parsing HTTP request arguments, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, Pyramid, webapp2, Falcon, and aiohttp.
https://webargs.readthedocs.io/
MIT License
1.38k stars 158 forks source link

Base MethodView with Flask #521

Closed dfilter closed 4 years ago

dfilter commented 4 years ago

I was wondering if there is a way to create a base flask MethodView that can be reused for multiple different models and schemas in conjunction with webargs. For example i have the below MethodView:

class DocumentQueryArgs(QueryArgsSchema):
    name = fields.Str()
    type = fields.Str()

class DocumentMethodView(MethodView):    
    @use_args(DocumentQueryArgs(), location='query')
    def get(self, args, id=None):
        if id:
            document = Document.find_by_id(id)
            return DocumentSchema().dump(document)

        response = Document.find_by(**args)
        response['records'] = DocumentSchema(many=True).dump(
            response['records'])
        return response

    @use_args(DocumentSchema(), location='json')
    def post(self, args):
        args.commit_to_db()
        return {
            'message': 'Document successfully created.',
            'record': DocumentSchema().dump(args)
        }, 201

    @use_args(DocumentSchema(), location='json')
    def put(self, args, id):
        args.commit_to_db()
        return {
            'message': 'Document successfully updated.',
            'record': DocumentSchema().dump(args)
        }

    def delete(self, id):
        document = Document.find_by_id(id)
        record = DocumentSchema().dump(document)
        document.delete()
        return {
            'message': 'Document successfully deleted.',
            'record': record
        }

My MethodView for my Product model and schema looks identical. I'd like to reduce duplicated code if possible.

sirosen commented 4 years ago

I'm missing a lot of context, but this seems like a general python question, more suited to StackOverflow than the webargs issue tracker? Is there a part of this which is particular to webargs, or is this a question about how to handle a kind of polymorphism?

If I've followed you correctly though, this may be a case where you want to use a webargs parser's parse method directly, not via the use_args decorator.

For example, I could define a type which inherits schemas as class attributes like so... ```python from flask import request from webargs.flaskparser import parser class MyAbstractType: query_schema = BaseQuerySchema body_schema = BaseBodySchema def get(self): args = parser.parse(self.query_schema, request, location="query") self._do_get(args) def _do_get(self, args): ... # some default def post(self): args = parser.parse(self.body_schema, request, location="json") self._do_post(args) def _do_post(self, args): ... # some default class MyConcreteTypeWithCustomBody: body_schema = CustomBodySchema def _do_post(self, args): ... # something specific ```

The use of parser.parse is covered in the first section of the quickstart, before use_args and use_kwargs: https://webargs.readthedocs.io/en/latest/quickstart.html


Can you clarify if there's some part of the question which is specific to webargs and could be answered in our docs? If so, we might be able to clarify or improve the docs.

lafrech commented 4 years ago

There's been a discussion here about this: https://github.com/marshmallow-code/flask-smorest/issues/79.

It is specific to a framework using webargs (flask-smorest) but I think it is relevant.

sirosen commented 4 years ago

People, including the OP here, want to establish some rigging for making CRUD APIs using standard HTTP methods, somehow mapped to schemas (and webargs locations) for each. Is that correct?

There are so many ways to slice and dice this issue, maybe what we really need is some well-supported pattern.

My example above is a bit simplistic, but if it's specifically about supporting CRUD as the set of actions, we could put together a more significant/robust example. Something which takes some specification of methods with schemas + locations, and lets you do something like (just thinking aloud)

class MyClass:
    crud_create_spec = {"json": BodySchema}

    @crud_create_args  # lots of magic in this decorator to inspect `self`
    def post(self, args):
        ...

?

dfilter commented 4 years ago

Thank you @sirosen for the suggestions.

People, including the OP here, want to establish some rigging for making CRUD APIs using standard HTTP methods, somehow mapped to schemas (and webargs locations) for each. Is that correct?

Yes this is correct. All the work is being done at the schema and model level, so my method views are all identical to one another baring the schemas they use and the messages they return. Though having time to reflect on my post I think you are correct that this is more of a question of polymorphism then anything specific with webargs. I'm still newish to the more advanced features of python classes so I'll probably need to do some research.

The use of parser.parse is covered in the first section of the quickstart, before use_args and use_kwargs

I think parser might be what I need moving forward. Thanks for the examples.

Edit: Thanks to the suggestion above I have something working! Thank you!