routers.py 5.79 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2 3 4 5
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
6
# Copyright © 2018 Mael Kervella
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

22
"""Defines the custom routers to generate the URLs of the API.
23 24 25
"""

from collections import OrderedDict
26

27 28 29 30 31 32 33 34 35
from django.conf.urls import url, include
from django.core.urlresolvers import NoReverseMatch
from rest_framework import views
from rest_framework.routers import DefaultRouter
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.schemas import SchemaGenerator
from rest_framework.settings import api_settings

36

37
class AllViewsRouter(DefaultRouter):
38 39 40 41
    """A router that can register both viewsets and views and generates
    a full API root page with all the generated URLs.
    """

42 43 44 45 46
    def __init__(self, *args, **kwargs):
        self.view_registry = []
        super(AllViewsRouter, self).__init__(*args, **kwargs)

    def register_viewset(self, *args, **kwargs):
47 48 49 50
        """Register a viewset in the router. Alias of `register` for
        convenience.

        See `register` in the base class for details.
51 52 53 54
        """
        return self.register(*args, **kwargs)

    def register_view(self, pattern, view, name=None):
55 56 57 58 59 60 61
        """Register a view in the router.

        Args:
            pattern: The URL pattern to use for this view.
            view: The class-based view to register.
            name: An optional name for the route generated. Defaults is
                based on the pattern last section (delimited by '/').
62 63 64 65 66 67
        """
        if name is None:
            name = self.get_default_name(pattern)
        self.view_registry.append((pattern, view, name))

    def get_default_name(self, pattern):
68 69 70 71 72 73 74 75
        """Returns the name to use for the route if none was specified.

        Args:
            pattern: The pattern for this route.

        Returns:
            The name to use for this route.
        """
76 77 78
        return pattern.split('/')[-1]

    def get_api_root_view(self, schema_urls=None):
79 80 81 82 83 84 85 86 87 88 89
        """Create a class-based view to use as the API root.

        Highly inspired by the base class. See details on the implementation
        in the base class. The only difference is that registered view URLs
        are added after the registered viewset URLs on this root API page.

        Args:
            schema_urls: A schema to use for the URLs.

        Returns:
            The view to use to display the root API page.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        """
        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)
        for pattern, view, name in self.view_registry:
            api_root_dict[pattern] = name

        view_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
        schema_media_types = []

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

        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(
                            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

                return Response(ret)

        return APIRoot.as_view()

    def get_urls(self):
147 148 149 150 151 152
        """Builds the list of URLs to register.

        Returns:
            A list of the URLs generated based on the viewsets registered
            followed by the URLs generated based on the views registered.
        """
153 154 155 156 157 158
        urls = super(AllViewsRouter, self).get_urls()

        for pattern, view, name in self.view_registry:
            urls.append(url(pattern, view.as_view(), name=name))

        return urls