I have an API in Django REST framework with the following nested resources
/wizard-api/industries/
/wizard-api/industries/<pk>/
/wizard-api/industries/<industry_pk>/sub-industries/
/wizard-api/industries/<industry_pk>/sub-industries/<pk>/
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/<pk>/
# basenames:
wizard-api:industries-list
wizard-api:industries-detail
wizard-api:sub-industries-list
wizard-api:sub-industries-detail
wizard-api:details-list
wizard-api:details-detail
Here my URLs config using drf-nested-routers:
# Nested Routes
first_level = routers.SimpleRouter()
first_level.register(r'industries', views.IndustryViewSet, basename='industries')
second_level = routers.NestedSimpleRouter(first_level, r'industries', lookup='industry')
second_level.register(r'sub-industries', views.SubIndustryViewSet, basename='sub-industries')
third_level = routers.NestedSimpleRouter(second_level, r'sub-industries', lookup='sub_industry')
third_level.register(r'details', views.SubIndustryDetailsViewSet, basename='abc')
I want to apply the HATEOAS principle
# endpoint: /wizard-api/industries/1/
# response:
{
"id": 1,
"name": "food and beverage",
"subindustries": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/"
}
I made it for the first level using the HyperlinkedIdentityField in the first Serializer
class IndustryModelSerializer(serializers.ModelSerializer):
subindustries = serializers.HyperlinkedIdentityField(
view_name='wizard-api:sub-industries-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = Industry
exclude = ['created', 'modified', 'active']
The problem appears when I try to apply the same logic in the subsequent levels, i.e: generating the url from the sub-industries level to the details level:
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/
I tried with the details field in the serializer of the second level (sub-industries):
class SubIndustryModelSerializer(serializers.ModelSerializer):
details = serializers.HyperlinkedIdentityField(
view_name='wizard-api:details-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = SubIndustry
exclude = ['created', 'modified', 'active']
The expected response is:
# endpoint: /wizard-api/industries/1/sub-industries/
# response:
[
{
"id": 1,
"name": "beverage industries",
"details": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/1/details/"
},
{
"id": 2,
"name": "food production",
"details": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/2/details/"
}
]
But I got the following error:
Could not resolve URL for hyperlinked relationship using view name "wizard-api:details-list". You may have failed to include the related model in your API, or incorrectly configured the
lookup_field
attribute on this field.
I made some tests and found the solution.
First, the best way to implement the nested routes in your case will be with these HyperlinkedModelSerializer, NestedHyperlinkedModelSerializer.
So the first level should look like this:
class IndustryModelSerializer(HyperlinkedModelSerializer):
subindustries = serializers.HyperlinkedIdentityField(
view_name='wizard-api:sub-industries-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = Industry
fields = ['subindustries', 'foo'...]
The main problem in the second level can be fixed like this:
class SubIndustryModelSerializer(NestedHyperlinkedModelSerializer):
parent_lookup_kwargs = {
'industry_pk': 'industry_id'
}
class Meta:
model = SubIndustry
fields = ['foo', 'url', ...]
extra_kwargs = {
'url': {
'view_name': 'wizard-api:details-list',
'lookup_url_kwarg': 'sub_industry_pk'
}
}
Based on your ERD, you must include a parent_kwargs for the proper configuration of nested routes, change the exclude for fields, and add the url parameter.
Hope it works!