diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 406dda72c..9bd3e192b 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -210,6 +210,42 @@ class TemplateHTMLRenderer(BaseRenderer): response.status_text.title())) +class JinjaTemplateRenderer(TemplateHTMLRenderer): + """ + Renders data to HTML for use with the Jinja2 template engine. + + The template name is determined by (in order of preference): + + 1. An explicit .template_name set on the response. + 2. An explicit .template_name set on this class. + 3. The return result of calling view.get_template_names(). + """ + + def render(self, data, accepted_media_type=None, renderer_context=None): + + renderer_context = renderer_context or {} + view = renderer_context['view'] + request = renderer_context['request'] + response = renderer_context['response'] + + # add the current user to the context + # otherwise it won't be available in templates + renderer_context['user'] = request.user + + # get template (jinja2) + if response.exception: + template = self.get_exception_template(response) + else: + template_names = self.get_template_names(response, view) + template = self.resolve_template(template_names) + + # return the dict containing the context + # for jinja2 to process. + # Would throw a value error if a + # RequestContext was returned + return template.render(renderer_context) + + # Note, subclass TemplateHTMLRenderer simply for the exception behavior class StaticHTMLRenderer(TemplateHTMLRenderer): """ diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 5c9a7ade1..b063b74bb 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -138,6 +138,79 @@ class NamespaceVersioning(BaseVersioning): return request.version + ':' + viewname +class MultipleNamespaceVersioning(NamespaceVersioning): + """ + This is the same as NamespaceVersioning, the difference is that + multiple namespaces can be used for bigger projects. + + In settings.py, REST_FRAMEWORK should include + ALLOWED_VERSIONS: a list of all available versions + DEFAULT_VERSION: a string containing the default version to use + + An example URL conf that is namespaced using different namespaces + + # item/urls.py + urlpatterns = [ + url(r'^/(?P[\d]+)/$', item_detail, name='detail'), + ] + + # urls.py + api_patterns = [ + url(r'^items', include('mysite.item.urls', namespace='items')), + ] + + urlpatterns = [ + url(r'^', include(api_patterns)), + url(r'^json/', include(api_patterns, namespace='json')), + ] + + They can then be accessed in templates such as + + - to get the default version + {% url 'items:detail' pk=2 %} + --> /items/2/ + + - to get the json version or any other version + {% url 'json:items:detail' pk=2 } + --> /json/items/2/ + + allowed versions must be set as it will be used to compare its + contents with the given namespaces of the url + """ + + def determine_version(self, request, *args, **kwargs): + resolver_match = getattr(request, 'resolver_match', None) + version = None + + if (resolver_match is None or not resolver_match.namespaces): + if (not resolver_match.namespace): + version = self.default_version + else: + version = resolver_match.namespace + + for namespace in resolver_match.namespaces: + if namespace in self.allowed_versions: + version = namespace + + if version is None: + version = self.default_version + + return version + + def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): + if request.version is not None: + viewname = self.get_versioned_viewname(viewname, request) + return super(NamespaceVersioning, self).reverse( + viewname, args, kwargs, request, format, **extra + ) + + def get_versioned_viewname(self, viewname, request): + if request.resolver_match is not None and request.resolver_match.namespaces: + return ':'.join(request.resolver_match.namespaces) + + return request.version + ':' + viewname + + class HostNameVersioning(BaseVersioning): """ GET /something/ HTTP/1.1