drf-extensions icon indicating copy to clipboard operation
drf-extensions copied to clipboard

DefaultRouterExtensible

Open cancan101 opened this issue 9 years ago • 4 comments

The issue right now is that if there are multiple applications within the same Django project, the root api view does not pull in all end points. This helps to solve that problem.

What do you think of pulling in something like: https://github.com/tomchristie/django-rest-framework/pull/2001 to drf-extensions?

The implementation would look something like:

class DefaultRouterExtensible(DefaultRouter):
    def __init__(self, require_authenticated=True, include_root_view=True, *args, **kwargs):
        super(DefaultRouterExtensible, self).__init__(*args, **kwargs)
        self.require_authenticated = require_authenticated
        self.include_root_view = include_root_view

    def get_api_root_dict(self):
        api_root_dict = OrderedDict()
        list_name = self.routes[0].name
        for prefix, viewset, basename in self.registry:
            api_root_dict[prefix] = list_name.format(basename=basename)
        return api_root_dict

    def get_api_root_view(self):
        """
        Return a view to use as the API root.
        """
        api_root_dict = self.get_api_root_dict()

        class APIRoot(views.APIView):
            permission_classes = (permissions.IsAuthenticated, ) if self.require_authenticated else ()

            _ignore_model_permissions = True

            def get(self, request, *args, **kwargs):
                ret = OrderedDict()
                namespace = get_resolver_match(request).namespace
                for key, url_name in api_root_dict.items():
                    if namespace:
                        url_name = namespace + ':' + url_name
                    try:
                        ret[key] = reverse(
                            url_name,
                            request=request,
                            format=kwargs.get('format', None)
                        )
                    except NoReverseMatch: # pragma: no cover
                        # Don't bail out if eg. no list routes exist, only detail routes.
                        continue

                return Response(ret)

        return APIRoot.as_view()


class DefaultRouterAddItem(DefaultRouterExtensible):
    def __init__(self, *args, **kwargs):
        super(DefaultRouterAddItem, self).__init__(*args, **kwargs)
        self._additional_api_root_dict = {}

    def add_routes_to_root(self, route_dict):
        duplicate_routes = set(self._additional_api_root_dict.keys()) & set(route_dict.keys())
        # Make sure we are not attempting to add a route that already exists
        assert not duplicate_routes, duplicate_routes
        self._additional_api_root_dict.update(route_dict)

    def get_api_root_dict(self):
        ret = super(DefaultRouterAddItem, self).get_api_root_dict()
        for k, v in self._additional_api_root_dict.iteritems():
            ret[k] = v
        return ret

cancan101 avatar May 12 '15 01:05 cancan101

I believe that extensible DefaultRoute should be in core of DRF.

chibisov avatar May 12 '15 08:05 chibisov

Unless @tomchristie has changed his mind since the last discussion, that won't be happening.

cancan101 avatar May 12 '15 14:05 cancan101

It wont. Routers and viewsets are already too complicated, and there's no reason variations on them can't instead be supported in third party packages.

tomchristie avatar May 13 '15 10:05 tomchristie

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

router = ContainerRouter() router.register_router(router1) router.register_router(router2)

urlpatterns = [ url(r'^api/', include(router.urls)), ]

auvipy avatar Dec 06 '15 15:12 auvipy