Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending custom router to default router across apps in Django Rest Framework

I have come across a problem regarding having the API apps seperate, while still being able to use the browsable API for navigation.

I have previously used a seperate routers.py file in my main application containing the following extension of the DefaultRouter.

class DefaultRouter(routers.DefaultRouter):
    def extend(self, router):
        self.registry.extend(router.registry)

Followed by adding the other application routers like this:

from . routers import DefaultRouter
from app1.urls import router as app1_router

# Default Router
mainAppRouter = DefaultRouter()
mainAppRouter.extend(app1_router)

where the app1_router is a new SimpleRouter object.

Now the problem occurs when I want to modify the SimpleRouter and create my own App1Router, such as this

class App1Router(SimpleRouter):

    routes = [
        Route(
            url = r'^{prefix}{trailing_slash}$',
            mapping = {
                'get': 'retrieve',
                'post': 'create',
                'patch': 'partial_update',
            },
            name = '{basename}-user',
            initkwargs = {}
        ),
    ]

This will not handle my extension correctly. As an example, GET and PATCH are not recognized as allowed methods whenever I extend the router, but when I dont extend, but only use the custom router, everything works fine.

My question is therefor, how can I handle extending custom routers across seperate applications, but still maintain a good browsable API?

like image 589
Gjert Avatar asked Jan 05 '18 20:01

Gjert


People also ask

What is default router in Django REST framework?

DefaultRouter. This router is similar to SimpleRouter as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional . json style format suffixes.

What is difference between APIView and ViewSet?

APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.

What is ModelViewSet in Django REST framework?

ModelViewSet. The ModelViewSet class inherits from GenericAPIView and includes implementations for various actions, by mixing in the behavior of the various mixin classes. The actions provided by the ModelViewSet class are .list() , .retrieve() , .create() , .update() , .partial_update() , and .destroy() .

What is Queryset in Django REST framework?

queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True . many - If applied to a to-many relationship, you should set this argument to True .


1 Answers

The router registry is just a standard python list, so you can call YourRouter.registry.extend() directly on registy withouth the need to subclass the DefaultRouter, check the source of DRF here . Since registry is a list, when you extend registry you add another python list on top of python list, which implies calling YourRouter.registry.extend(app_router.registry). What worked for me, was importing routers from another apps (SimpleRouters) and adding them on top of default router registry.

#aplication_root/urls.py
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from app_3.urls import router as app_3_router


router = DefaultRouter()
router.registry.extend(app_3_router.registry)

urlpatterns = [

    path('api/', include(router.urls)),

]

If your are trying to add versioning to your app by adding the prefix, I suggest to take a look at versioning schema available in DRF - maybe it could fit your needs DRF versioning

for example with URLPathVersioning enabled in your settings

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
}

urlparttern list from snipped above would look like that

urlpatterns = [

    path('api/<version>/', include(router.urls)),
]
like image 80
Maciej Słoniewski Avatar answered Oct 08 '22 09:10

Maciej Słoniewski