Exemple #1
0
    def get_lookup_routes(self, viewset):
        ret = [self.routes[0]]
        # Determine any `@action` or `@link` decorated methods on the viewset
        dynamic_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, "bind_to_methods", None)
            if httpmethods:
                httpmethods = [method.lower() for method in httpmethods]
                dynamic_routes.append((httpmethods, methodname))

        for route in self.lookups_routes:
            if route.mapping == {"{httpmethod}": "{methodname}"}:
                # Dynamic routes (@link or @action decorator)
                for httpmethods, methodname in dynamic_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    mapping = dict((httpmethod, methodname) for httpmethod in httpmethods)
                    name = routers.replace_methodname(route.name, methodname)
                    if "extra_lookup_fields" in initkwargs:
                        uri = route.url[1]
                        uri = routers.replace_methodname(uri, methodname)
                        ret.append(
                            routers.Route(url=uri, mapping=mapping, name="%s-extra" % name, initkwargs=initkwargs)
                        )
                    uri = routers.replace_methodname(route.url[0], methodname)
                    ret.append(routers.Route(url=uri, mapping=mapping, name=name, initkwargs=initkwargs))
            else:
                # Standard route
                ret.append(route)
        return ret
Exemple #2
0
    def get_routes(self, viewset):
        ret = super(APIRouter, self).get_routes(viewset)

        toplevel_dynamic_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, 'toplevel_bind_to_methods', None)
            if httpmethods:
                httpmethods = [method.lower() for method in httpmethods]
                toplevel_dynamic_routes.append((httpmethods, methodname))
        
        extra_ret = []
        for route in self.routes:
            if route.mapping == {'{httpmethod}': '{toplevelmethodname}'}:
                for httpmethods, methodname in toplevel_dynamic_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    extra_ret.append(Route(
                        url=routers.replace_methodname(route.url, methodname),
                        mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                        name=routers.replace_methodname(route.name, methodname),
                        initkwargs=initkwargs,
                    ))

        extra_ret.extend(ret)

        return extra_ret
Exemple #3
0
    def compute_routes(self, wrapper, method_name, route):
        httpmethods = wrapper.bind_to_methods

        dynamic_routes = []
        if httpmethods:
            if method_name in self.known_actions():
                raise ImproperlyConfigured('Cannot use @rest_method decorator on '
                                           'method "%s" as it is an existing route' % method_name)
            httpmethods = [method.lower() for method in httpmethods]
            dynamic_routes.append((httpmethods, method_name))


        # Dynamic routes (@link or @action decorator)
        instance_routes = []
        for httpmethods, method_name in dynamic_routes:
            initkwargs = route.initkwargs.copy()
            # initkwargs.update(getattr(viewset, method_name).kwargs)
            instance_routes.append(Route(
                url=replace_methodname(route.url, method_name),
                mapping=dict((httpmethod, method_name) for httpmethod in httpmethods),
                name=replace_methodname(route.name, method_name),
                initkwargs=initkwargs,
            ))

        return instance_routes
    def get_routes(self, viewset):
        """
        Augment `self.routes` with any dynamically generated routes.

        Returns a list of the Route namedtuple.
        """

        known_actions = routers.flatten([route.mapping.values() for route in self.routes])

        # Determine any `@action` or `@link` decorated methods on the viewset
        dynamic_route_mappings = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, 'bind_to_methods', None)
            if httpmethods:
                if methodname in known_actions:
                    raise ImproperlyConfigured('Cannot use @action or @link decorator on '
                                               'method "%s" as it is an existing route' % methodname)
                httpmethods = [method.lower() for method in httpmethods]
                dynamic_route_mappings.append((httpmethods, methodname))


        dynamic_routes = []
        standard_routes = []
        dyn_url_pattern = r'^{prefix}/{methodname}{trailing_slash}$'
        for route in self.routes:
            if route.mapping == {'{httpmethod}': '{methodname}'}:
                # Dynamic routes (@link or @action decorator)
                for httpmethods, methodname in dynamic_route_mappings:
                    initkwargs = route.initkwargs.copy()
                    view_fn = getattr(viewset, methodname)
                    initkwargs.update(view_fn.kwargs)

                    # Does the dynamic route have a pk argument?
                    pk_in_args = 'pk' in inspect.getargspec(view_fn).args

                    if pk_in_args:
                        # Default DRF behavior: prefix/pk/methodname
                        dynamic_routes.append(routers.Route(
                            url=routers.replace_methodname(route.url, methodname),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=routers.replace_methodname(route.name, methodname),
                            initkwargs=initkwargs,
                        ))
                    else:
                        # No pk? Then map to prefix/methodname
                        dynamic_routes.append(routers.Route(
                            url=routers.replace_methodname(dyn_url_pattern, methodname),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=routers.replace_methodname('{basename}-{methodname}', methodname),
                            initkwargs=initkwargs,
                        ))
            else:
                # Standard route
                standard_routes.append(route)

        return dynamic_routes + standard_routes
Exemple #5
0
 def get_dynamic_routes_instances(self, viewset, route, dynamic_routes):
     dynamic_routes_instances = []
     for httpmethods, methodname, endpoint, is_for_list in dynamic_routes:
         initkwargs = route.initkwargs.copy()
         initkwargs.update(getattr(viewset, methodname).kwargs)
         dynamic_routes_instances.append(Route(
             url=replace_methodname(route.url, endpoint),
             mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
             name=replace_methodname(route.name, endpoint),
             initkwargs=initkwargs,
         ))
     return dynamic_routes_instances
Exemple #6
0
 def get_dynamic_routes_instances(self, viewset, route, dynamic_routes):
     dynamic_routes_instances = []
     for httpmethods, methodname, endpoint, is_for_list in dynamic_routes:
         initkwargs = route.initkwargs.copy()
         initkwargs.update(getattr(viewset, methodname).kwargs)
         dynamic_routes_instances.append(Route(
             url=replace_methodname(route.url, endpoint),
             mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
             name=replace_methodname(route.name, endpoint),
             initkwargs=initkwargs,
         ))
     return dynamic_routes_instances
Exemple #7
0
    def get_routes(self, viewset):
        """
        Augment `self.routes` with any dynamically generated routes.

        Returns a list of the Route namedtuple.
        """

        known_actions = flatten([route.mapping.values() for route in self.routes])

        # Determine any `@action` or `@link` decorated methods on the viewset
        dynamic_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, 'bind_to_methods', None)
            if httpmethods:
                if methodname in known_actions:
                    raise ImproperlyConfigured('Cannot use @action or @link decorator on '
                                               'method "%s" as it is an existing route' % methodname)
                httpmethods = [method.lower() for method in httpmethods]
                dynamic_routes.append((httpmethods, methodname))

        ret = []
        for route in self.routes:
            if route.mapping == {'{httpmethod}': '{methodname}'}:
                # Dynamic routes (@link or @action decorator)
                for httpmethods, methodname in dynamic_routes:
                    method_kwargs = getattr(viewset, methodname).kwargs
                    url_path = method_kwargs.pop("url_path", None) or methodname
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(method_kwargs)
                    if getattr(getattr(viewset, methodname), 'for_list', False):
                        ret.insert(0, Route(
                            url=replace_methodname(route.url, url_path).replace('{lookup}/', ''),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=replace_methodname(route.name, url_path),
                            initkwargs=initkwargs,
                        ))
                    else:
                        ret.append(Route(
                            url=replace_methodname(route.url, url_path),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=replace_methodname(route.name, url_path),
                            initkwargs=initkwargs,
                        ))
            else:
                # Standard route
                ret.append(route)

        return ret
Exemple #8
0
    def get_routes(self, viewset):
        """Return a list of routers.Route namedtuples that correspond
        to the routes for the given viewset.
        """
        answer = super(Router, self).get_routes(viewset)

        # Iterate over the methods on the viewset and look for any
        # base methods.
        #
        # Note: We have a confusing situation here because the use of the
        # term "method" is overloaded; it refers both to HTTP methods
        # (e.g. GET, POST) and class/instance methods in Python.  In order
        # to try and gain some clarity, the former are consistently referred
        # to in this code block as "HTTP methods".
        for method_name in dir(viewset):
            method = getattr(viewset, method_name)

            # Sanity check: If this viewset method doesn't have a
            # `base_http_methods` attribute, we're done.
            # Must support recent versions of DRF
            bind_methods = getattr(method, 'base_http_methods', getattr(method, 'bind_to_methods', None))
            if bind_methods is None:
                continue

            # Determine the HTTP methods, URL pattern, and name pattern.
            http_methods = [i.lower() for i in bind_methods]
            url_format = r'^{prefix}/{methodname}{trailing_slash}$'
            url = routers.replace_methodname(url_format, method_name)
            name_format = '{basename}-{methodnamehyphen}' 
            name = routers.replace_methodname(name_format, method_name)

            # Create the actual Route object.
            route = routers.Route(
                url=url,
                mapping=dict([(i, method_name) for i in http_methods]),
                name=name,
                initkwargs=copy(method.kwargs),
            )

            # Append the route to the answer.
            answer.append(route)

        # Done!
        return answer
Exemple #9
0
        def _get_dynamic_routes(route, dynamic_routes):
            ret = []
            for httpmethods, methodname in dynamic_routes:
                method_kwargs = getattr(viewset, methodname).kwargs
                initkwargs = route.initkwargs.copy()
                initkwargs.update(method_kwargs)
                url_path = initkwargs.pop("url_path", None) or methodname
                ret.append(
                    Route(
                        url=replace_methodname(route.url, url_path),
                        mapping={
                            httpmethod: methodname
                            for httpmethod in httpmethods
                        },
                        name=replace_methodname(route.name, url_path),
                        initkwargs=initkwargs,
                    ))

            return ret
Exemple #10
0
    def get_lookup_routes(self, viewset):
        ret = [self.routes[0]]
        # Determine any `@action` or `@link` decorated methods on the viewset
        dynamic_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, 'bind_to_methods', None)
            if httpmethods:
                httpmethods = [method.lower() for method in httpmethods]
                dynamic_routes.append((httpmethods, methodname))

        for route in self.lookups_routes:
            if route.mapping == {'{httpmethod}': '{methodname}'}:
                # Dynamic routes (@link or @action decorator)
                for httpmethods, methodname in dynamic_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    mapping = dict(
                        (httpmethod, methodname) for httpmethod in httpmethods)
                    name = routers.replace_methodname(route.name, methodname)
                    if 'extra_lookup_fields' in initkwargs:
                        uri = route.url[1]
                        uri = routers.replace_methodname(uri, methodname)
                        ret.append(
                            routers.Route(
                                url=uri,
                                mapping=mapping,
                                name='%s-extra' % name,
                                initkwargs=initkwargs,
                            ))
                    uri = routers.replace_methodname(route.url[0], methodname)
                    ret.append(
                        routers.Route(
                            url=uri,
                            mapping=mapping,
                            name=name,
                            initkwargs=initkwargs,
                        ))
            else:
                # Standard route
                ret.append(route)
        return ret
Exemple #11
0
    def get_routes(self, viewset):
        """Return a list of routers.Route namedtuples that correspond
        to the routes for the given viewset.
        """
        answer = super(Router, self).get_routes(viewset)

        # Iterate over the methods on the viewset and look for any
        # base methods.
        #
        # Note: We have a confusing situation here because the use of the
        # term "method" is overloaded; it refers both to HTTP methods
        # (e.g. GET, POST) and class/instance methods in Python.  In order
        # to try and gain some clarity, the former are consistently referred
        # to in this code block as "HTTP methods".
        for method_name in dir(viewset):
            method = getattr(viewset, method_name)

            # Sanity check: If this viewset method doesn't have a
            # `base_http_methods` attribute, we're done.
            if not hasattr(method, 'base_http_methods'):
                continue

            # Determine the HTTP methods, URL pattern, and name pattern.
            http_methods = [i.lower() for i in method.base_http_methods]
            url_format = r'^{prefix}/{methodname}{trailing_slash}$'
            url = routers.replace_methodname(url_format, method_name)
            name_format = '{basename}-{methodnamehyphen}'
            name = routers.replace_methodname(name_format, method_name)

            # Create the actual Route object.
            route = routers.Route(
                url=url,
                mapping=dict([(i, method_name) for i in http_methods]),
                name=name,
                initkwargs=copy(method.kwargs),
            )

            # Append the route to the answer.
            answer.append(route)

        # Done!
        return answer
    def _get_dynamic_route(self, route, action, known_routes={}):
        initkwargs = route.initkwargs.copy()
        initkwargs.update(action.kwargs)
        url_path = escape_curly_brackets(action.url_path)
        if helpers.rest_framework_version >= (3, 9, 0):
            route_kwargs = {
                'url': route.url.replace('{url_path}', url_path),
                'mapping': action.mapping,
                'name': route.name.replace('{url_name}', action.url_name),
                'trailing_slash': initkwargs.pop("trailing_slash", None),
                'detail': route.detail,
                'initkwargs': initkwargs
            }
        else:
            # DEPRECATED: drf < 3.9
            route_kwargs = {
                'url': replace_methodname(route.url, url_path),
                'mapping': action.mapping,
                'name': replace_methodname(route.name, action.url_name),
                'trailing_slash': initkwargs.pop("trailing_slash", None),
                'detail': action.detail,
                'initkwargs': initkwargs,
            }
        # сверяемся, чтоб не было дубликатов в router
        if not known_routes.get(route_kwargs['url'], None):
            return RouteDrfs(**route_kwargs)

        known_mapping = known_routes[route_kwargs['url']]['mapping']
        for httpmethod in [
                'get', 'post', 'put', 'patch', 'delete', 'head', 'options',
                'trace'
        ]:
            if not isinstance(route_kwargs['mapping'].get(httpmethod, None),
                              str):
                continue
            if isinstance(known_mapping.get(httpmethod, None), str):
                raise ImproperlyConfigured(
                    'Cannot map to url "%s" method "%s". Already exists. Try another url or method'
                    % (route_kwargs['url'], httpmethod))

        return RouteDrfs(**route_kwargs)
Exemple #13
0
    def get_relation_routes(self, viewset):
        """
        Generate routes to serve relational objects. This method will add
        a sub-URL for each relational field.

        e.g.
        A viewset for the following serializer:

          class UserSerializer(..):
              events = DynamicRelationField(EventSerializer, many=True)
              groups = DynamicRelationField(GroupSerializer, many=True)
              location = DynamicRelationField(LocationSerializer)

        will have the following URLs added:

          /users/<pk>/events/
          /users/<pk>/groups/
          /users/<pk>/location/
        """

        routes = []

        if not hasattr(viewset, 'serializer_class'):
            return routes

        serializer = viewset.serializer_class()
        fields = getattr(serializer, 'get_link_fields', lambda: [])()

        route_name = '{basename}-{methodnamehyphen}'

        for field_name, field in six.iteritems(fields):
            has_list_related = hasattr(viewset, 'list_related')
            has_create_related = hasattr(viewset, 'create_related')
            url = (
                r'^{prefix}/{lookup}/(?P<field_name>%s)'
                '{trailing_slash}$' % field_name
            )
            mapping = {}
            if has_list_related:
                mapping['get'] = 'list_related'
            if has_create_related:
                mapping['post'] = 'create_related'
            if mapping:
                routes.append(Route(
                    url=url,
                    mapping=mapping,
                    name=replace_methodname(route_name, field_name),
                    initkwargs={}
                ))
        return routes
    def get_routes(self, viewset):
        ret = []

        dynamic_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, 'bind_to_collection_methods', None)
            if httpmethods:
                httpmethods = [method.lower() for method in httpmethods]
                dynamic_routes.append((httpmethods, methodname))

        route = self.collection_route
        for httpmethods, methodname in dynamic_routes:
            initkwargs = route.initkwargs.copy()
            initkwargs.update(getattr(viewset, methodname).kwargs)
            ret.append(Route(
                url=replace_methodname(route.url, methodname),
                mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                name=replace_methodname(route.name, methodname),
                initkwargs=initkwargs,
            ))

        ret.extend(super(CollectionMethodRouterMixin, self).get_routes(viewset))
        return ret
Exemple #15
0
    def get_relation_routes(self, viewset):
        """
        Generate routes to serve relational objects. This method will add
        a sub-URL for each relational field.

        e.g.
        A viewset for the following serializer:

          class UserSerializer(..):
              events = DynamicRelationField(EventSerializer, many=True)
              groups = DynamicRelationField(GroupSerializer, many=True)
              location = DynamicRelationField(LocationSerializer)

        will have the following URLs added:

          /users/<pk>/events/
          /users/<pk>/groups/
          /users/<pk>/location/
        """

        routes = []

        if not hasattr(viewset, 'serializer_class'):
            return routes
        if not hasattr(viewset, 'list_related'):
            return routes

        serializer = viewset.serializer_class()
        fields = getattr(serializer, 'get_link_fields', lambda: [])()

        route_name = '{basename}-{methodnamehyphen}'
        if drf_version >= (3, 8, 0):
            route_compat_kwargs = {'detail': False}
        else:
            route_compat_kwargs = {}

        for field_name, field in iter(fields.items()):
            methodname = 'list_related'
            url = (r'^{prefix}/{lookup}/(?P<field_name>%s)'
                   '{trailing_slash}$' % field_name)
            routes.append(
                Route(url=url,
                      mapping={'get': methodname},
                      name=replace_methodname(route_name, field_name),
                      initkwargs={},
                      **route_compat_kwargs))
        return routes
Exemple #16
0
    def get_relation_routes(self, viewset):
        """
        Generate routes to serve relational objects. This method will add
        a sub-URL for each relational field.

        e.g.
        A viewset for the following serializer:

          class UserSerializer(..):
              events = DynamicRelationField(EventSerializer, many=True)
              groups = DynamicRelationField(GroupSerializer, many=True)
              location = DynamicRelationField(LocationSerializer)

        will have the following URLs added:

          /users/<pk>/events/
          /users/<pk>/groups/
          /users/<pk>/location/
        """

        routes = []

        if not hasattr(viewset, 'serializer_class'):
            return routes
        if not hasattr(viewset, 'list_related'):
            return routes

        serializer = viewset.serializer_class()
        fields = getattr(serializer, 'get_link_fields', lambda: [])()

        route_name = '{basename}-{methodnamehyphen}'

        for field_name, field in six.iteritems(fields):
            methodname = 'list_related'
            url = (
                r'^{prefix}/{lookup}/(?P<field_name>%s)'
                '{trailing_slash}$' % field_name
            )
            routes.append(Route(
                url=url,
                mapping={'get': methodname},
                name=replace_methodname(route_name, field_name),
                initkwargs={}
            ))
        return routes
Exemple #17
0
def replace_listmethodname(format_string, methodname):
    ret = replace_methodname(format_string, methodname)
    ret = ret.replace('{listmethodname}', methodname)
    return ret
    def get_routes(self, viewset):
        """
        Augment `self.routes` with any dynamically generated routes.

        Returns a list of the Route namedtuple.
        """

        known_actions = flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)])

        # Determine any `@detail_route` or `@list_route` decorated methods on the viewset
        detail_routes = []
        list_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, "bind_to_methods", None)
            detail = getattr(attr, "detail", True)
            if httpmethods:
                if methodname in known_actions:
                    raise ImproperlyConfigured(
                        "Cannot use @detail_route or @list_route "
                        'decorators on method "%s" '
                        "as it is an existing route" % methodname
                    )
                httpmethods = [method.lower() for method in httpmethods]
                if detail:
                    detail_routes.append((httpmethods, methodname))
                else:
                    list_routes.append((httpmethods, methodname))

        ret = []
        for route in self.routes:
            if isinstance(route, DynamicDetailRoute):
                # Dynamic detail routes (@detail_route decorator)
                for httpmethods, methodname in detail_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    ret.append(
                        Route(
                            url=replace_methodname(route.url, methodname),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=replace_methodname(route.name, methodname),
                            initkwargs=initkwargs,
                        )
                    )
            elif isinstance(route, DynamicListRoute):
                # Dynamic list routes (@list_route decorator)
                for httpmethods, methodname in list_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    ret.append(
                        Route(
                            url=replace_methodname(route.url, methodname),
                            mapping=dict((httpmethod, methodname) for httpmethod in httpmethods),
                            name=replace_methodname(route.name, methodname),
                            initkwargs=initkwargs,
                        )
                    )
            else:
                # Standard route
                ret.append(route)

        return ret
Exemple #19
0
def replace_collectionmethodname(format_string, methodname):
    ret = replace_methodname(format_string, methodname)
    ret = ret.replace('{collectionmethodname}', methodname)
    return ret
Exemple #20
0
    def get_routes(self, viewset):
        """
        Augment `self.routes` with any dynamically generated routes.

        Returns a list of the Route namedtuple.
        """

        known_actions = flatten([
            route.mapping.values() for route in self.routes
            if isinstance(route, Route)
        ])

        # Determine any `@detail_route` or `@list_route` decorated methods on the viewset
        detail_routes = []
        list_routes = []
        for methodname in dir(viewset):
            attr = getattr(viewset, methodname)
            httpmethods = getattr(attr, "bind_to_methods", None)
            detail = getattr(attr, "detail", True)
            if httpmethods:
                if methodname in known_actions:
                    raise ImproperlyConfigured(
                        "Cannot use @detail_route or @list_route "
                        'decorators on method "%s" '
                        "as it is an existing route" % methodname)
                httpmethods = [method.lower() for method in httpmethods]
                if detail:
                    detail_routes.append((httpmethods, methodname))
                else:
                    list_routes.append((httpmethods, methodname))

        ret = []
        for route in self.routes:
            if isinstance(route, DynamicDetailRoute):
                # Dynamic detail routes (@detail_route decorator)
                for httpmethods, methodname in detail_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    ret.append(
                        Route(
                            url=replace_methodname(route.url, methodname),
                            mapping=dict((httpmethod, methodname)
                                         for httpmethod in httpmethods),
                            name=replace_methodname(route.name, methodname),
                            initkwargs=initkwargs,
                        ))
            elif isinstance(route, DynamicListRoute):
                # Dynamic list routes (@list_route decorator)
                for httpmethods, methodname in list_routes:
                    initkwargs = route.initkwargs.copy()
                    initkwargs.update(getattr(viewset, methodname).kwargs)
                    ret.append(
                        Route(
                            url=replace_methodname(route.url, methodname),
                            mapping=dict((httpmethod, methodname)
                                         for httpmethod in httpmethods),
                            name=replace_methodname(route.name, methodname),
                            initkwargs=initkwargs,
                        ))
            else:
                # Standard route
                ret.append(route)

        return ret