mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-02 11:30:12 +03:00
Merge 1d161687a2
into d2994e0596
This commit is contained in:
commit
be3d8c10df
|
@ -309,6 +309,9 @@ class HyperlinkedRelatedField(RelatedField):
|
|||
if hasattr(obj, 'pk') and obj.pk in (None, ''):
|
||||
return None
|
||||
|
||||
if hasattr(request, 'app_name') and request.app_name is not None:
|
||||
view_name = request.app_name + ':' + view_name
|
||||
|
||||
lookup_value = getattr(obj, self.lookup_field)
|
||||
kwargs = {self.lookup_url_kwarg: lookup_value}
|
||||
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||
|
|
|
@ -147,8 +147,9 @@ class SimpleRouter(BaseRouter):
|
|||
),
|
||||
]
|
||||
|
||||
def __init__(self, trailing_slash=True):
|
||||
def __init__(self, trailing_slash=True, app_name=None):
|
||||
self.trailing_slash = '/' if trailing_slash else ''
|
||||
self.app_name = app_name
|
||||
super(SimpleRouter, self).__init__()
|
||||
|
||||
def get_default_base_name(self, viewset):
|
||||
|
@ -285,6 +286,7 @@ class SimpleRouter(BaseRouter):
|
|||
initkwargs.update({
|
||||
'basename': basename,
|
||||
'detail': route.detail,
|
||||
'router': self,
|
||||
})
|
||||
|
||||
view = viewset.as_view(mapping, **initkwargs)
|
||||
|
|
|
@ -385,9 +385,14 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
|
|||
|
||||
if hasattr(cls._module, 'urlpatterns'):
|
||||
cls._module_urlpatterns = cls._module.urlpatterns
|
||||
if hasattr(cls._module, 'app_name'):
|
||||
cls._module_app_name = cls._module.app_name
|
||||
|
||||
cls._module.urlpatterns = cls.urlpatterns
|
||||
|
||||
if hasattr(cls, 'app_name'):
|
||||
cls._module.app_name = cls.app_name
|
||||
|
||||
cls._override.enable()
|
||||
super(URLPatternsTestCase, cls).setUpClass()
|
||||
|
||||
|
@ -400,3 +405,9 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
|
|||
cls._module.urlpatterns = cls._module_urlpatterns
|
||||
else:
|
||||
del cls._module.urlpatterns
|
||||
|
||||
if hasattr(cls, '_module_app_name'):
|
||||
cls._module.app_name = cls._module_app_name
|
||||
else:
|
||||
if hasattr(cls._module, 'app_name'):
|
||||
del cls._module.app_name
|
||||
|
|
|
@ -63,6 +63,9 @@ class ViewSetMixin(object):
|
|||
# value is provided by the router through the initkwargs.
|
||||
cls.basename = None
|
||||
|
||||
# Setting a router allows optional resolution of the app_name
|
||||
cls.router = None
|
||||
|
||||
# actions must not be empty
|
||||
if not actions:
|
||||
raise TypeError("The `actions` argument must be provided when "
|
||||
|
@ -99,6 +102,9 @@ class ViewSetMixin(object):
|
|||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
if self.router is not None:
|
||||
request.app_name = self.router.app_name
|
||||
|
||||
# And continue as usual
|
||||
return self.dispatch(request, *args, **kwargs)
|
||||
|
||||
|
@ -115,6 +121,7 @@ class ViewSetMixin(object):
|
|||
view.cls = cls
|
||||
view.initkwargs = initkwargs
|
||||
view.suffix = initkwargs.get('suffix', None)
|
||||
view.router = initkwargs.get('router', None)
|
||||
view.actions = actions
|
||||
return csrf_exempt(view)
|
||||
|
||||
|
|
85
tests/test_relations_hyperlink_appname.py
Normal file
85
tests/test_relations_hyperlink_appname.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import pytest
|
||||
from django.conf.urls import include, url
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework import routers, serializers, viewsets
|
||||
from rest_framework.test import APIRequestFactory, URLPatternsTestCase
|
||||
from rest_framework.utils import json
|
||||
|
||||
factory = APIRequestFactory()
|
||||
request = factory.get('/') # Just to ensure we have a request in the serializer context
|
||||
|
||||
|
||||
class Wine(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class WineSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Wine
|
||||
fields = ('url', 'title')
|
||||
|
||||
|
||||
class WineViewSet(viewsets.ModelViewSet):
|
||||
queryset = Wine.objects.all()
|
||||
serializer_class = WineSerializer
|
||||
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'wines', WineViewSet)
|
||||
|
||||
|
||||
class TestHyperlinkedRouterNoName(URLPatternsTestCase, TestCase):
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^api/', include(router.urls)),
|
||||
]
|
||||
|
||||
def test_no_name_works(self):
|
||||
w = Wine(title="Shiraz")
|
||||
w.save()
|
||||
|
||||
response = self.client.get('/api/wines/')
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content.decode('utf-8')) == [{'title': 'Shiraz', 'url': 'http://testserver/api/wines/1/'}]
|
||||
|
||||
|
||||
# Failing case with Django 2.0 and HyperlinkedModelSerializer
|
||||
class TestHyperlinkedRouterFailsWithName(URLPatternsTestCase, TestCase):
|
||||
urlpatterns = [
|
||||
url(r'^api2/', include((router.urls, 'appname2'))),
|
||||
]
|
||||
|
||||
def test_hyperlink_fails(self):
|
||||
w = Wine(title="Shiraz")
|
||||
w.save()
|
||||
|
||||
with pytest.raises(
|
||||
ImproperlyConfigured,
|
||||
message='Could not resolve URL for hyperlinked relationship using view '
|
||||
'name "wine-detail". You may have failed to include the related model in '
|
||||
'your API, or incorrectly configured the `lookup_field` attribute on this field.'):
|
||||
|
||||
self.client.get('/api2/wines/')
|
||||
|
||||
|
||||
router2 = routers.DefaultRouter(app_name='appname2')
|
||||
router2.register(r'wines', WineViewSet)
|
||||
|
||||
|
||||
class TestHyperlinkedRouterConfigured(URLPatternsTestCase, TestCase):
|
||||
urlpatterns = [
|
||||
url(r'^api2/', include((router2.urls, 'appname2'))),
|
||||
]
|
||||
|
||||
def test_regex_url_path_list(self):
|
||||
w = Wine(title="Shiraz")
|
||||
w.save()
|
||||
|
||||
response = self.client.get('/api2/wines/')
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content.decode('utf-8')) == [{'title': 'Shiraz', 'url': 'http://testserver/api2/wines/1/'}]
|
Loading…
Reference in New Issue
Block a user