4

I have chosen the Django REST Framework and drf-nested-routers to build an API for Products and ProductReports. Here are the relevant classes:

# models.py
from django.db import models


class Product(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)


class ProductReport(models.Model):
    user_name = models.CharField(max_length=256, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    product = models.ForeignKey('Product', default=1)

...

# serializers.py
from rest_framework import serializers
from models import Product, ProductReport


class ProductSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Product


class ProductReportSerializer(serializers.HyperlinkedModelSerializer):

    product = ProductSerializer()

    class Meta:
        model = ProductReport

...

# views.py
from django.shortcuts import render
from rest_framework import viewsets, mixins
from rest_framework.response import Response
from models import Product, ProductReport
from serializers import ProductSerializer, ProductReportSerializer


class ProductViewSet(mixins.RetrieveModelMixin,
                   mixins.ListModelMixin,
                   viewsets.GenericViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer


class ProductReportViewSet(viewsets.ViewSet):
    queryset = ProductReport.objects.all()
    serializer_class = ProductReportSerializer

    def list(self, request, product_pk=None):
        queryset = self.queryset.filter(product=product_pk)
        serializer = ProductReportSerializer(queryset, many=True, context={'request': request})
        return Response(serializer.data)

    def retrieve(self, request, pk=None, product_pk=None):
        queryset = self.queryset.get(pk=pk, product=product_pk)
        serializer = ProductReportSerializer(queryset)
        return Response(serializer.data)

...

# urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from rest_framework import routers
from rest_framework_nested.routers import SimpleRouter, NestedSimpleRouter
from productreports import views

router = routers.SimpleRouter()
router.register(r'products', views.ProductViewSet)

products_router = NestedSimpleRouter(router, r'products', lookup='product')
products_router.register(r'productreports', views.ProductReportViewSet)


urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    url(r'^', include(products_router.urls)),
    url(r'^admin/', include(admin.site.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
)

When I visit http://localhost:8000/products/1/productreports/ the following error is shown:

ImproperlyConfigured at /products/1/productreports/
Could not resolve URL for hyperlinked relationship using view name "productreport-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.

There are a few things I am unsure about if they are correctly set up:

  • product = models.ForeignKey('Product', default=1)
  • many=True in serializer = ProductReportSerializer(queryset, many=True, context={'request': request}) ... each product can have many product reports
  • The ProductReportSerializer in general
  • lookup='product' in products_router = NestedSimpleRouter(router, r'products', lookup='product')

Update

I noticed that the error only occurs when there is at least one associated productreport for a product. If the productreports table contains no entry for a particular product then the API response is fine.

When I inspect the serializer in ProductReportViewSet#list I get:

ProductReportSerializer([
    <ProductReport: ProductReport object>, <ProductReport: ProductReport object>, 
    <ProductReport: ProductReport object>, <ProductReport: ProductReport object>, 
    <ProductReport: ProductReport object>, <ProductReport: ProductReport object>, 

'...(remaining elements truncated)...'], context={'request': <rest_framework.request.Request object>}, many=True):
        url = HyperlinkedIdentityField(view_name='productreport-detail')
        product = ProductSerializer():
            url = HyperlinkedIdentityField(view_name='product-detail')
            created_at = DateTimeField(read_only=True)
        user_name = CharField(max_length=256)
        created_at = DateTimeField(read_only=True)
2
  • A bit irrelevant to a question...I happened not to use the solution in production. What I may recommend is to try this another project that is well documented, maintained and tesed: chibisov.github.io/drf-extensions/docs/#nested-routes Commented Mar 20, 2015 at 6:28
  • 1
    @Sekai Somewhat. I decided not to use drf-nested-routers and instead minimize routes Commented May 22, 2015 at 8:40

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.