mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 01:47:59 +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):
 | 
			
		||||
        if basename is None:
 | 
			
		||||
            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))
 | 
			
		||||
 | 
			
		||||
        # invalidate the urls cache
 | 
			
		||||
        if hasattr(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):
 | 
			
		||||
        """
 | 
			
		||||
        If `basename` is not specified, attempt to automatically determine
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,11 @@ class RouterTestModel(models.Model):
 | 
			
		|||
    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):
 | 
			
		||||
    url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +47,11 @@ class KWargedNoteViewSet(viewsets.ModelViewSet):
 | 
			
		|||
    lookup_url_kwarg = 'text'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BasenameViewSet(viewsets.ModelViewSet):
 | 
			
		||||
    queryset = BasenameTestModel.objects.all()
 | 
			
		||||
    serializer_class = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockViewSet(viewsets.ModelViewSet):
 | 
			
		||||
    queryset = None
 | 
			
		||||
    serializer_class = None
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +166,7 @@ class TestSimpleRouter(URLPatternsTestCase, TestCase):
 | 
			
		|||
    def test_register_after_accessing_urls(self):
 | 
			
		||||
        self.router.register(r'notes', NoteViewSet)
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -481,3 +491,103 @@ class TestViewInitkwargs(URLPatternsTestCase, TestCase):
 | 
			
		|||
        initkwargs = match.func.initkwargs
 | 
			
		||||
 | 
			
		||||
        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