vitalik / django-ninja

💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
https://django-ninja.dev
MIT License
7.36k stars 437 forks source link

Parent URL resolver match parameters aren't handled (except when they are) #373

Open oliwarner opened 2 years ago

oliwarner commented 2 years ago

I have a soft tenant system where clients of the system each have their own /company-slug/... base URL. Probably not how I'd approach this today but this is where we are. All our URLs have a component that gets extracted and churned into a Company record (so we can do permissions checks, etc). I'm mounting my ninja API underneath that.

path('<cs:company_slug>/', include([
    # ...
    path('api/', api.urls),
])

That works great —I can access the API— but there are two problems.

The first is accessing parent parameters (eg company_slug) from within the API views. I need to know what the company is. I could probably ship this out to middleware, stuff the company object into the request (and possibly will, long term) but in other scenarios that might not be desirable. For now I'm accessing it via request.resolver_match.kwargs['company_slug']. It'd be nice if I could access this from the view more directly. Maybe.

The buggier problem is that docs don't work when mounted underneath additional parameters. The docs view at /mycompany/api/docs gets an extra, unexpected keyword argument (company_slug in my example here) and just pops.

ievans3024 commented 2 years ago

+1 on the doc generation bug. The simple solution would be to add *args, **kwargs to the function signature(s) for builtin views so that django can pass in developer-defined URL params without ninja throwing an error.

andres-k commented 1 year ago

Did you end up resolving this somehow in a way that the swagger ui and openapi docks pick it up correctly?

vitalik commented 1 year ago

@oliwarner @ievans3024 @andres-k

The first is accessing parent parameters (eg company_slug) from within the API view

you can do that like this (using Path marker):

api = NinjaAPI()

@api.get('/some')
def some_operation(request, q_param: str = '', company_slug: str = Path(..., include_in_schema=False)):
    return {'q_param': q_param, 'company_slug': company_slug}

as for the docs views, I guess there is no workaround - you can just craete two extra views taht will render docs html and openapi json under the url

vitalik commented 1 year ago

include_in_schema=False is important:

... company_slug: str = Path(..., include_in_schema=False)