python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.16k stars 335 forks source link

How to separate routes classes from namespace to multiple files? #529

Open SomeAkk opened 1 year ago

SomeAkk commented 1 year ago

Problem I got 1 namespace and some routes in it. Each route - class with @ns.xxx(...) decorators. My problem: routes classes are big and they lay in one file where namespase is. I want to separate route classes to files and than add them to namespace in centrilized manner.

When i try to solve that problem i catch myself by the tail: i need instantiated namespace when i just start to create it.

Code example main.py

from views.dog import Dog <- try to import class which needs ns, which not created
...
ns = Namespace('zoo')
ns.add_resource(Dog, '/dog')
ns.add_resource(...)

api = Api(version='1.0', title='Zoo', description='Zoo', prefix='/v1')
api.add_namespace(ns)

views/dog.py

from main import ns

class Dog(Resource):

    @ns.marshal_with(schema)
    @ns.expect(request_args)
    def get(self):
        ...some code which use ns
        ns.logger('dog: woof-woof')
        ... staff

How to separate namespace and resources in it to different files?

peter-doggart commented 1 year ago

Unfortunately, I don't think there is any easy way around the circular import. The namespace decorators are almost designed to be in the same file as the Resource classes. I normally opt to move all the business logic outside the Views files into another to make it smaller.

dshein-alt commented 1 year ago

In my project I use separate name spaces and combine it all together in one API. Route classes it self is just a wrappers that handles data validation and serialization and call an endpoint class from separate files that do the job.

Each name space routes file is small and easy to extend. Not sure it is actual solution for you, but my project structure proved itself to be useful, being over 2 years in production and extending continuously.

You could see sources here https://git.altlinux.org/gears/a/altrepo-api.git.

MajorDallas commented 8 months ago

Somehow this works:

api/
  __init__.py
  namespace/
    __init__.py
    resources.py
# namespace/resources.py

from api.namespace import namespace
from flask_restx import Resource

@namespace.route(...)
class Foo(Resource):
    ...
# namespace/__init__.py

from flask_restx import Namespace

namespace = Namespace(...)

from . import resources
# api/__init__.py

from flask_restx import Api

from . import namespace

api = Api(...)

api.add_namespace(namespace.namespace)

Basically, by moving the import of resources after creating the namespace, the namespace is initialized when the resource tries to refer to it, even though the module is not yet "fully initialized". Don't you love dynamic languages? 😜

This mostly hasn't caused me any problems except when using linters that try to organize your imports for you. I have to make sure every late import has a # isort:skip # noqa after it so that isort and black will leave it alone and pyright won't complain.

On further thought, I'm pretty sure the late import of resources isn't even necessary. I think I had some other reason for doing that which I no longer remember.