Neil
Neil

Reputation: 7202

Django REST Framework: Two different ModelSerializers for the same model

I have one model for which I've defined two different HyperlinkedModelSerializers:

class Foo(models.Model):
    ...

class FooSerializer1(serializers.HyperlinkedModelSerializer):
    ...

    class Meta:
        model = Foo
        fields = ('url', 'id', ...)
        lookup_field= 'pk'

# A second view of the same model for another API use-case
class FooSerializer2(serializers.HyperlinkedModelSerializer):
    ...

    class Meta:
        model = Foo
        fields = ('url', 'id', ...)
        lookup_field= 'pk'

FooSerializer1 is being used by a couple GenericViews in one Django app (i.e. its own urls.py), and FooSerializer2 by a ModelViewSet in another Django app. I have the ModelViewSet registered in a DefaultRouter along with a few other viewsets:

urlpatterns = patterns('',
    url(r'^$', 'myapp.views.api_root'),
    url(r'^foo1/$', views.FooList1.as_view(), name='foo1-list'),
    ...
)

urlpatterns = format_suffix_patterns(urlpatterns)

...

class FooViewSet2(viewsets.ReadOnlyModelViewSet):
    queryset = Foo.objects.all()
    serializer_class = FooSerializer2
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    def get_queryset(self):
        ...

router = routers.DefaultRouter()
...
router.register(r'foo2', views.FooViewSet2)
...
urlpatterns = router.urls

That router's auto-generated api root displays the endpoint for the GenericView of FooSerializer1 (foo1-list), instead of foo2/. If I manually GET foo2/, the results show Foo serialized according to FooSerializer2 (which is correct), however the URL for each result again displays the foo1 detail view.

I tried setting get_serializer in FooViewSet2, but that didn't work. How do I get the api-root and FooSerializer2 results to display the URLs corresponding to FooViewset2?

Upvotes: 6

Views: 2648

Answers (1)

Neil
Neil

Reputation: 7202

DRF fortunately allows the flexibility to support this scenario through setting a few parameters.

First, I set the basename parameter on the router entry:

router.register(r'foo2', views.Foo2ViewSet, 'foo2')

Next, I set the view_name in the HyperlinkedModelSerializers in order to not default to foo-detail as mentioned in the docs:

class FooSerializer1(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='foo1-detail',
    )
    ...

urlpatterns = patterns('',
    url(r'^$', 'myapp.views.api_root'),
    url(r'^foo1/$', views.Foo1List.as_view(), name='foo1-list'),
    url(r'^foo1/(?P<pk>[0-9]+)/$', views.FooDetail1.as_view(), name='foo1-detail'),

    ...

class FooSerializer2(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='foo2-detail',
    )
    ...

Upvotes: 10

Related Questions