mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
raise ImproperlyConfigured exception if basename
is not unique (#8438)
* raise ImproperlyConfigured if basename already exists * rename already_registered function; return True/False * additional basename tests * additional basename tests * Update rest_framework/routers.py Co-authored-by: David Graves <dgraves@lrtcapitalgroup.com> Co-authored-by: Asif Saif Uddin <auvipy@gmail.com>
This commit is contained in:
parent
b79099f7ba
commit
48a21aa0eb
|
@ -52,12 +52,24 @@ class BaseRouter:
|
||||||
def register(self, prefix, viewset, basename=None):
|
def register(self, prefix, viewset, basename=None):
|
||||||
if basename is None:
|
if basename is None:
|
||||||
basename = self.get_default_basename(viewset)
|
basename = self.get_default_basename(viewset)
|
||||||
|
|
||||||
|
if self.is_already_registered(basename):
|
||||||
|
msg = (f'Router with basename "{basename}" is already registered. '
|
||||||
|
f'Please provide a unique basename for viewset "{viewset}"')
|
||||||
|
raise ImproperlyConfigured(msg)
|
||||||
|
|
||||||
self.registry.append((prefix, viewset, basename))
|
self.registry.append((prefix, viewset, basename))
|
||||||
|
|
||||||
# invalidate the urls cache
|
# invalidate the urls cache
|
||||||
if hasattr(self, '_urls'):
|
if hasattr(self, '_urls'):
|
||||||
del self._urls
|
del self._urls
|
||||||
|
|
||||||
|
def is_already_registered(self, new_basename):
|
||||||
|
"""
|
||||||
|
Check if `basename` is already registered
|
||||||
|
"""
|
||||||
|
return any(basename == new_basename for _prefix, _viewset, basename in self.registry)
|
||||||
|
|
||||||
def get_default_basename(self, viewset):
|
def get_default_basename(self, viewset):
|
||||||
"""
|
"""
|
||||||
If `basename` is not specified, attempt to automatically determine
|
If `basename` is not specified, attempt to automatically determine
|
||||||
|
|
|
@ -21,6 +21,11 @@ class RouterTestModel(models.Model):
|
||||||
text = models.CharField(max_length=200)
|
text = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
|
||||||
|
class BasenameTestModel(models.Model):
|
||||||
|
uuid = models.CharField(max_length=20)
|
||||||
|
text = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
|
||||||
class NoteSerializer(serializers.HyperlinkedModelSerializer):
|
class NoteSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')
|
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')
|
||||||
|
|
||||||
|
@ -42,6 +47,11 @@ class KWargedNoteViewSet(viewsets.ModelViewSet):
|
||||||
lookup_url_kwarg = 'text'
|
lookup_url_kwarg = 'text'
|
||||||
|
|
||||||
|
|
||||||
|
class BasenameViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = BasenameTestModel.objects.all()
|
||||||
|
serializer_class = None
|
||||||
|
|
||||||
|
|
||||||
class MockViewSet(viewsets.ModelViewSet):
|
class MockViewSet(viewsets.ModelViewSet):
|
||||||
queryset = None
|
queryset = None
|
||||||
serializer_class = None
|
serializer_class = None
|
||||||
|
@ -156,7 +166,7 @@ class TestSimpleRouter(URLPatternsTestCase, TestCase):
|
||||||
def test_register_after_accessing_urls(self):
|
def test_register_after_accessing_urls(self):
|
||||||
self.router.register(r'notes', NoteViewSet)
|
self.router.register(r'notes', NoteViewSet)
|
||||||
assert len(self.router.urls) == 2 # list and detail
|
assert len(self.router.urls) == 2 # list and detail
|
||||||
self.router.register(r'notes_bis', NoteViewSet)
|
self.router.register(r'notes_bis', NoteViewSet, basename='notes_bis')
|
||||||
assert len(self.router.urls) == 4
|
assert len(self.router.urls) == 4
|
||||||
|
|
||||||
|
|
||||||
|
@ -481,3 +491,103 @@ class TestViewInitkwargs(URLPatternsTestCase, TestCase):
|
||||||
initkwargs = match.func.initkwargs
|
initkwargs = match.func.initkwargs
|
||||||
|
|
||||||
assert initkwargs['basename'] == 'routertestmodel'
|
assert initkwargs['basename'] == 'routertestmodel'
|
||||||
|
|
||||||
|
|
||||||
|
class BasenameTestCase:
|
||||||
|
def test_conflicting_autogenerated_basenames(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with the same model, and no basename specified
|
||||||
|
throws an ImproperlyConfigured exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet)
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_kwduplicate', KWargedNoteViewSet)
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_duplicate', NoteViewSet)
|
||||||
|
|
||||||
|
def test_conflicting_mixed_basenames(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with the same model, and no basename specified on 1
|
||||||
|
throws an ImproperlyConfigured exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet)
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_kwduplicate', KWargedNoteViewSet, basename='routertestmodel')
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_duplicate', NoteViewSet, basename='routertestmodel')
|
||||||
|
|
||||||
|
def test_nonconflicting_mixed_basenames(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with the same model, and a distinct basename
|
||||||
|
specified on the second router does not fail
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet)
|
||||||
|
self.router.register(r'notes_kwduplicate', KWargedNoteViewSet, basename='routertestmodel_kwduplicate')
|
||||||
|
self.router.register(r'notes_duplicate', NoteViewSet, basename='routertestmodel_duplicate')
|
||||||
|
|
||||||
|
def test_conflicting_specified_basename(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with the same model, and the same basename specified
|
||||||
|
on both throws an ImproperlyConfigured exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet, basename='notes')
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_kwduplicate', KWargedNoteViewSet, basename='notes')
|
||||||
|
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_duplicate', KWargedNoteViewSet, basename='notes')
|
||||||
|
|
||||||
|
def test_nonconflicting_specified_basename(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with the same model, and a distinct basename specified
|
||||||
|
on each does not throw an exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet, basename='notes')
|
||||||
|
self.router.register(r'notes_kwduplicate', KWargedNoteViewSet, basename='notes_kwduplicate')
|
||||||
|
self.router.register(r'notes_duplicate', NoteViewSet, basename='notes_duplicate')
|
||||||
|
|
||||||
|
def test_nonconflicting_specified_basename_different_models(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with different models, and a distinct basename specified
|
||||||
|
on each does not throw an exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet, basename='notes')
|
||||||
|
self.router.register(r'notes_basename', BasenameViewSet, basename='notes_basename')
|
||||||
|
|
||||||
|
def test_conflicting_specified_basename_different_models(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with different models, and a conflicting basename specified
|
||||||
|
throws an exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet)
|
||||||
|
with pytest.raises(ImproperlyConfigured):
|
||||||
|
self.router.register(r'notes_basename', BasenameViewSet, basename='routertestmodel')
|
||||||
|
|
||||||
|
def test_nonconflicting_autogenerated_basename_different_models(self):
|
||||||
|
"""
|
||||||
|
Ensure 2 routers with different models, and a distinct basename specified
|
||||||
|
on each does not throw an exception
|
||||||
|
"""
|
||||||
|
self.router.register(r'notes', NoteViewSet)
|
||||||
|
self.router.register(r'notes_basename', BasenameViewSet)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDuplicateBasenameSimpleRouter(BasenameTestCase, TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.router = SimpleRouter(trailing_slash=False)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDuplicateBasenameDefaultRouter(BasenameTestCase, TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.router = DefaultRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDuplicateBasenameDefaultRouterRootViewName(BasenameTestCase, TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.router = DefaultRouter()
|
||||||
|
self.router.root_view_name = 'nameable-root'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user