{"id":3470,"library":"drf-nested-routers","title":"DRF Nested Routers","description":"DRF Nested Routers is a Python library that extends Django REST Framework (DRF) to facilitate the creation of nested resources. It provides specialized routers and relation fields necessary for building a full REST URL structure where one resource hierarchically depends on another (e.g., `/authors/{author_pk}/books/`). This is crucial when child resources logically exist only within the context of their parent. The library is actively maintained, with the current version being 0.95.0, and typically releases updates to maintain compatibility with new Django and DRF versions.","status":"active","version":"0.95.0","language":"en","source_language":"en","source_url":"https://github.com/alanjds/drf-nested-routers","tags":["django","drf","rest-framework","routing","nested-resources","api"],"install":[{"cmd":"pip install drf-nested-routers","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Required for the web framework integration.","package":"Django","optional":false},{"reason":"The core framework that this library extends.","package":"djangorestframework","optional":false}],"imports":[{"symbol":"routers","correct":"from rest_framework_nested import routers"},{"note":"Optional: Only needed when creating hyperlinks for nested relations in serializers.","symbol":"NestedHyperlinkedModelSerializer","correct":"from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer"},{"note":"While related, `NestedViewSetMixin` for automatic filtering by parent lookups is provided by `drf-extensions`, not `drf-nested-routers`.","wrong":"from rest_framework_nested.mixins import NestedViewSetMixin","symbol":"NestedViewSetMixin","correct":"from rest_framework_extensions.mixins import NestedViewSetMixin"}],"quickstart":{"code":"from django.db import models\nfrom rest_framework import viewsets, serializers\nfrom rest_framework_nested import routers\nfrom django.urls import path, include\n\n# models.py\nclass Domain(models.Model):\n    name = models.CharField(max_length=255)\n\n    def __str__(self):\n        return self.name\n\nclass Nameserver(models.Model):\n    domain = models.ForeignKey(Domain, related_name='nameservers', on_delete=models.CASCADE)\n    hostname = models.CharField(max_length=255)\n\n    def __str__(self):\n        return f'{self.hostname} ({self.domain.name})'\n\n# serializers.py (Optional, for hyperlinked relations)\nclass NameserverSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Nameserver\n        fields = ['id', 'hostname']\n\nclass DomainSerializer(serializers.ModelSerializer):\n    nameservers = NameserverSerializer(many=True, read_only=True)\n    class Meta:\n        model = Domain\n        fields = ['id', 'name', 'nameservers']\n\n# views.py\nclass DomainViewSet(viewsets.ModelViewSet):\n    queryset = Domain.objects.all()\n    serializer_class = DomainSerializer\n\nclass NameserverViewSet(viewsets.ModelViewSet):\n    serializer_class = NameserverSerializer\n\n    def get_queryset(self):\n        return self.queryset.filter(domain=self.kwargs['domain_pk'])\n\n    def perform_create(self, serializer):\n        domain = Domain.objects.get(pk=self.kwargs['domain_pk'])\n        serializer.save(domain=domain)\n\n# urls.py\nrouter = routers.DefaultRouter()\nrouter.register(r'domains', DomainViewSet, basename='domain')\n\ndomains_router = routers.NestedDefaultRouter(router, r'domains', lookup='domain')\ndomains_router.register(r'nameservers', NameserverViewSet, basename='domain-nameserver')\n\nurlpatterns = [\n    path('', include(router.urls)),\n    path('', include(domains_router.urls)),\n]\n\n# Example usage in a Django project's main urls.py:\n# from django.urls import path, include\n# from myapp import urls as myapp_urls\n# urlpatterns = [\n#     path('api/', include(myapp_urls)),\n# ]","lang":"python","description":"This quickstart demonstrates how to set up nested routes for `Domains` and their `Nameservers`. It uses `DefaultRouter` for the base resource and `NestedDefaultRouter` for the nested resource. The `lookup` argument in `NestedDefaultRouter` specifies the URL keyword argument that will be used to filter child resources (e.g., `domain_pk`). The `basename` argument is crucial for correctly generating URL patterns and reverse lookups, especially for the nested router."},"warnings":[{"fix":"Upgrade your Python, Django, and Django REST Framework versions to meet the minimum requirements of `drf-nested-routers` 0.94.2+: Python >=3.9, Django >=4.2, DRF >=3.14. Always consult the project's `requirements.txt` or GitHub README for precise compatibility.","message":"Version 0.94.2 and later dropped support for Python 3.8 and older, Django 4.1 and older, and DRF 3.13 and older. Version 0.93.5 dropped support for Python 3.6, Django 1.11, and DRF 3.6.","severity":"breaking","affected_versions":"<0.94.2 for Python 3.8, Django <4.2, DRF <3.14; <0.93.5 for Python 3.7, Django <3.2, DRF <3.14"},{"fix":"Ensure `lookup='parent'` in `Nested*Router` matches the `{parent_pk}` in the URL pattern. Your child `ViewSet`'s `get_queryset` method will access this via `self.kwargs['parent_pk']`. For example, `lookup='domain'` expects `domain_pk` in the URL and `self.kwargs['domain_pk']` in the ViewSet.","message":"The `lookup` parameter in `NestedSimpleRouter` or `NestedDefaultRouter` must match the URL keyword argument expected for the parent resource in the URL pattern and the corresponding lookup in the child ViewSet.","severity":"gotcha","affected_versions":"All versions"},{"fix":"In your `NestedHyperlinkedModelSerializer`, add `parent_lookup_kwargs = {'parent_pk': 'parent__pk'}` (or similar, matching your model relationships and URL lookups).","message":"When using `NestedHyperlinkedModelSerializer` for nested hyperlinks, you must define `parent_lookup_kwargs` in its `Meta` class to correctly build the parent resource's URL. This maps URL keyword arguments to model fields.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always explicitly define `basename='your-resource-name'` when calling `router.register()` to ensure correct URL pattern generation and reverse lookup functionality. This helps DRF correctly name the URL patterns. For example, `domains_router.register(r'nameservers', NameserverViewSet, basename='domain-nameserver')`.","message":"When registering a ViewSet with `Nested*Router.register()`, particularly if the ViewSet does not have a `queryset` attribute or a clear `model` attribute (e.g., a custom `ViewSet`), you *must* provide a `basename` argument.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}