@list_route和@detail_route嵌套路由不会出现在Browseable API中

时间:2015-04-10 18:36:23

标签: python django django-rest-framework

我试图通过@list_route@detail_route装饰器使用嵌套路由。路由工作并返回数据,但我必须在地址栏中手动导航到它们。它们不会出现在可浏览API中DefaultRouter生成的ApiRoot中。我正在使用Django 1.8和Rest Framework 3.1.1。

urls.py

router = DefaultRouter()
router.register(r'aggregates', viewsets.AggregateViewSet, base_name='aggregate')

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

viewsets.py

class AggregateViewSet(viewsets.GenericViewSet):
    queryset = models.DataAggregate.objects.order_by('id')
    serializer_class = serializers.AggregateSerializer

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)

        if page:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @list_route(url_path='recent')
    def recent_aggregates(self, request):
        return Response({'message': 'herp a derp'})

当我导航到/myapp/api时,可浏览的API仅显示以下内容:

HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS

{
    "aggregates": "http://localhost:8000/myapp/api/aggregates/"
}

期待这个:

{
    "aggregates": "http://localhost:8000/myapp/api/aggregates/"
    "aggregates-recent": "http://localhost:8000/myapp/api/aggregates/recent"
}

我尝试了各种修改版本,看看它是否会让步,无济于事。同样,这些路线功能,如果我手动导航它们,可浏览的API将显示它们的页面......但是会破坏可浏览api的目的。

我看了DefaultRouter(和SimpleRouter)的代码,它似乎确实发现了动态路线......

1 个答案:

答案 0 :(得分:2)

这是真的,并且默认情况下它不会出现在API中。

我自己使用该代码段来提供HAL样式(py3代码)之后的嵌套路由。

import urllib
from collections import OrderedDict

from rest_framework import serializers, relations

class SubNamespaceURLField(relations.HyperlinkedIdentityField):
    """Refers to a child namespace of the object, as pointed by view_name
    """
    def __init__(self, namespace, *args, **kwargs):
        self.namespace = namespace.strip('/')
        super().__init__(*args, **kwargs)

    def field_to_native(self, obj, field_name):
        base = super().field_to_native(obj, field_name)
        return urllib.parse.urljoin(base, self.namespace) + '/'

class HALNestedLinksField(relations.HyperlinkedIdentityField):
    """ Tries to represent a list of nested links on the resource a
    HAL-compliant way.

    See http://stateless.co/hal_specification.html
    """
    def __init__(self, endpoints, *args, **kwargs):
        """
        :param endpoints list of url suffixes leading to nested operations on
                         the resource (ex: ['preview', 'check'])
        """
        self.endpoints = endpoints
        super().__init__(*args, **kwargs)

    def get_attribute(self, obj):
        return obj

    def to_representation(self, value):
        links = OrderedDict()
        prefix = super().to_representation(self.get_attribute(value))
        for i in self.endpoints:
            # We consider if it contains a dot its a content-type indication,
            # so no trailing slash
            if '.' in i:
                suffix = ''
            else:
                suffix = '/'
            links[i] = {'href':
                        urllib.parse.urljoin(prefix, i) + suffix}
        return links

然后在序列化程序中使用它:

class SomeModelSerializer(serializers.HyperlinkedModelSerializer):
    _links = HALNestedLinksField(['revalidate'], # you detail_route names
                                 view_name='somemodel-detail')
    class Meta:
        model = SomeModel
        fields = ('url',
                  'date',
                  '_links') # do not forget it

您将获得_links属性,其中包含您在HALNestedLinksField中声明的所有相关路线。