使用Django的REST框架可浏览API与API的看法?

问题描述 投票:37回答:6

如果我有这样一个观点:

class MyAPIView(APIView):
    def get(self, request, name=None):
        return {"hello": name or "world"}

我怎样才能获得,其中包括在生成的文档吗?具体来说,我怎样才能得到它包含在API的根,所以当我访问“http://example.com/api/”出现?

与描述的APIView的documentation includes an example,但没有描述的实际上得到它包含在API浏览器中的进程。

django-rest-framework
6个回答
17
投票

在生成的文档?

嗨大卫,第一次了,我就不是很描述可浏览API为“生成的文档”。

如果您需要静态的文档你最好还是考虑一下像django-rest-swagger第三方工具。

可浏览的API意味着你构建的API将是自描述的,但它是从传统的静态文档工具一点点不同。可浏览的API确保所有您在API中创建端点能够与机器可读(即JSON)和人类可读(即HTML)表示响应两者。这也保证了你可以直接通过浏览器全面的互动 - 您通常使用的编程客户端交互的任何网址也将能与浏览器友好查看到API响应的。

我怎样才能像包括在内。

只是一个文档字符串添加到视图,它就会被列入的可浏览的API表示无论你的URL路由这一观点。

默认情况下,你可以使用降价符号包括在描述HTML标记,但你也可以customise that behaviour,例如,如果你想用第一个。

具体来说,我怎样才能得到它包含在API的根。

您只需要将网址明确添加到成无论大家认为你已经连接好于/api/返回的响应。例如...

from rest_framework import renderers
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.reverse import reverse


class APIRoot(APIView):
    def get(self, request):
        # Assuming we have views named 'foo-view' and 'bar-view'
        # in our project's URLconf.
        return Response({
            'foo': reverse('foo-view', request=request),
            'bar': reverse('bar-view', request=request)
        })

29
投票

到与基于以这样的方式使得API根同时显示与在APIRoot视图最少的代码视图我写延伸defaultrouter中并覆盖get_urls和get_api_root_view定制路由器的路由器和APIView类或方法混合;它看起来像如下:

from rest_framework import routers, views, reverse, response

class HybridRouter(routers.DefaultRouter):
    def __init__(self, *args, **kwargs):
        super(HybridRouter, self).__init__(*args, **kwargs)
        self._api_view_urls = {}

    def add_api_view(self, name, url):
        self._api_view_urls[name] = url

    def remove_api_view(self, name):
        del self._api_view_urls[name]

    @property
    def api_view_urls(self):
        ret = {}
        ret.update(self._api_view_urls)
        return ret

    def get_urls(self):
        urls = super(HybridRouter, self).get_urls()
        for api_view_key in self._api_view_urls.keys():
            urls.append(self._api_view_urls[api_view_key])
        return urls

    def get_api_root_view(self):
        # Copy the following block from Default Router
        api_root_dict = {}
        list_name = self.routes[0].name
        for prefix, viewset, basename in self.registry:
            api_root_dict[prefix] = list_name.format(basename=basename)
        api_view_urls = self._api_view_urls

        class APIRoot(views.APIView):
            _ignore_model_permissions = True

            def get(self, request, format=None):
                ret = {}
                for key, url_name in api_root_dict.items():
                    ret[key] = reverse.reverse(url_name, request=request, format=format)
                # In addition to what had been added, now add the APIView urls
                for api_view_key in api_view_urls.keys():
                    ret[api_view_key] = reverse.reverse(api_view_urls[api_view_key].name, request=request, format=format)
                return response.Response(ret)

        return APIRoot.as_view()

然后,我用它作为 -

router = routers.HybridRouter()
router.register(r'abc', views.ABCViewSet)
router.add_api_view("api-view", url(r'^aview/$', views.AView.as_view(), name='aview-name'))
urlpatterns = patterns('',
    url(r'^api/', include(router.urls)),

3
投票

我已经优化HybridRouter我用例和删除了一些代码。看看这个:

class HybridRouter(routers.DefaultRouter):
    def __init__(self, *args, **kwargs):
        super(HybridRouter, self).__init__(*args, **kwargs)
        self.view_urls = []

    def add_url(self, view):
        self.view_urls.append(view)

    def get_urls(self):
        return super(HybridRouter, self).get_urls() + self.view_urls

    def get_api_root_view(self):
        original_view = super(HybridRouter, self).get_api_root_view()

        def view(request, *args, **kwargs):
            resp = original_view(request, *args, **kwargs)
            namespace = request.resolver_match.namespace
            for view_url in self.view_urls:
                name = view_url.name
                url_name = name
                if namespace:
                    url_name = namespace + ':' + url_name
                resp.data[name] = reverse(url_name,
                                          args=args,
                                          kwargs=kwargs,
                                          request=request,
                                          format=kwargs.get('format', None))
            return resp
        return view

与用法:

router = routers.HybridRouter(trailing_slash=False)
router.add_url(url(r'^me', v1.me.view, name='me'))
router.add_url(url(r'^status', v1.status.view, name='status'))

urlpatterns = router.urls

要么:

router = routers.HybridRouter(trailing_slash=False)
router.view_urls = [
    url(r'^me', v1.me.view, name='me'),
    url(r'^status', v1.status.view, name='status'),
]

urlpatterns = router.urls

3
投票

的@imyousuf代码更新版本3.4.1 DRF工作。

class HybridRouter(routers.DefaultRouter):
    def __init__(self, *args, **kwargs):
        super(HybridRouter, self).__init__(*args, **kwargs)
        self._api_view_urls = {}

    def add_api_view(self, name, url):
        self._api_view_urls[name] = url

    def remove_api_view(self, name):
        del self._api_view_urls[name]

    @property
    def api_view_urls(self):
        ret = {}
        ret.update(self._api_view_urls)
        return ret

    def get_urls(self):
        urls = super(HybridRouter, self).get_urls()
        for api_view_key in self._api_view_urls.keys():
            urls.append(self._api_view_urls[api_view_key])
        return urls

    def get_api_root_view(self, api_urls=None):
        # Copy the following block from Default Router
        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)

        view_renderers = list(self.root_renderers)
        schema_media_types = []

        if api_urls and self.schema_title:
            view_renderers += list(self.schema_renderers)
            schema_generator = SchemaGenerator(
                title=self.schema_title,
                url=self.schema_url,
                patterns=api_urls
            )
            schema_media_types = [
                renderer.media_type
                for renderer in self.schema_renderers
                ]

        api_view_urls = self._api_view_urls

        class APIRoot(views.APIView):
            _ignore_model_permissions = True
            renderer_classes = view_renderers

            def get(self, request, *args, **kwargs):
                if request.accepted_renderer.media_type in schema_media_types:
                    # Return a schema response.
                    schema = schema_generator.get_schema(request)
                    if schema is None:
                        raise exceptions.PermissionDenied()
                    return Response(schema)

                # Return a plain {"name": "hyperlink"} response.
                ret = OrderedDict()
                namespace = request.resolver_match.namespace
                for key, url_name in api_root_dict.items():
                    if namespace:
                        url_name = namespace + ':' + url_name
                    try:
                        ret[key] = reverse.reverse(
                            url_name,
                            args=args,
                            kwargs=kwargs,
                            request=request,
                            format=kwargs.get('format', None)
                        )
                    except NoReverseMatch:
                        # Don't bail out if eg. no list routes exist, only detail routes.
                        continue

                # In addition to what had been added, now add the APIView urls
                for api_view_key in api_view_urls.keys():
                    url_name = api_view_urls[api_view_key].name
                    if namespace:
                        url_name = namespace + ':' + url_name
                    ret[api_view_key] = reverse.reverse(url_name, request=request, format=kwargs.get('format'))

                return response.Response(ret)

        return APIRoot.as_view()

如何使用:

mobile_router = HybridRouter()
mobile_router.add_api_view("device", url(r'^device/register/$', DeviceViewSet.as_view({'post': 'register'}), name='device-register'))

1
投票

通过@imyousuf解决方案是好的,但它不支持URL命名空间和有点过时。

下面是它的更新:

class HybridRouter(routers.DefaultRouter):
    def __init__(self, *args, **kwargs):
        super(HybridRouter, self).__init__(*args, **kwargs)
        self._api_view_urls = {}

    def add_api_view(self, name, url):
        self._api_view_urls[name] = url

    def remove_api_view(self, name):
        del self._api_view_urls[name]

    @property
    def api_view_urls(self):
        ret = {}
        ret.update(self._api_view_urls)
        return ret

    def get_urls(self):
        urls = super(HybridRouter, self).get_urls()
        for api_view_key in self._api_view_urls.keys():
            urls.append(self._api_view_urls[api_view_key])
        return urls

    def get_api_root_view(self):

        # Copy the following block from Default Router
        api_root_dict = {}
        list_name = self.routes[0].name
        for prefix, viewset, basename in self.registry:
            api_root_dict[prefix] = list_name.format(basename=basename)

        # In addition to that:
        api_view_urls = self._api_view_urls

        class APIRoot(views.APIView):
            _ignore_model_permissions = True

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

                # In addition to what had been added, now add the APIView urls
                for api_view_key in api_view_urls.keys():
                    namespace = request.resolver_match.namespace
                    if namespace:
                        url_name = namespace + ":" + api_view_key
                    ret[api_view_key] = reverse(url_name,
                                        args=args,
                                        kwargs=kwargs,
                                        request=request,
                                        format=kwargs.get('format', None))

                return response.Response(ret)

        return APIRoot.as_view()

1
投票

为了记录在案,这是2019现在https://bitbucket.org/hub9/django-hybrid-router仍在工作,唯一的修改是该行64已被编辑成为:

                regex = api_view_urls[api_view_key].pattern.regex
© www.soinside.com 2019 - 2024. All rights reserved.