Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add a HyperLink in django-rest-framework to a ModelViewSet

Is it possible in Django Rest Framework to add a HyperLink on the id field of each record in the ModelViewSet List View that links to the Detail View for that record?

The documentation says to add a view_name argument, but is this possible to do under a ModelViewSet because the views don't actually have names?

like image 223
Aaron Lelevier Avatar asked Aug 14 '15 18:08

Aaron Lelevier


People also ask

What is ModelViewSet in Django REST framework?

ModelViewSet. The ModelViewSet class inherits from GenericAPIView and includes implementations for various actions, by mixing in the behavior of the various mixin classes. The actions provided by the ModelViewSet class are .list() , .retrieve() , .create() , .update() , .partial_update() , and .destroy() .

What is the difference between ViewSet and ModelViewSet?

A ViewSet is a class-based view, able to handle all of the basic HTTP requests: GET, POST, PUT, DELETE without hard coding any of the logic. The viewsets module also provides a ModelViewSet class that maps HTTP request actions to CRUD actions in the Database.

What is Hyperlink serializer?

HyperlinkedModelSerializer is a layer of abstraction over the default serializer that allows to quickly create a serializer for a model in Django. Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds.


2 Answers

first thing is that ALL ModelViewSet urls have names, even if you don´t set those names explicitly.

You can find out how the default urls are created within a router, in the docs: http://www.django-rest-framework.org/api-guide/routers/ (see 'SimpleRouter' url names table)

To see all the actual url names available in your application, try with this utility:

def print_url_pattern_names(patterns):
    """Print a list of urlpattern and their names"""
    for pat in patterns:
        if pat.__class__.__name__ == 'RegexURLResolver':            # load patterns from this RegexURLResolver
            print_url_pattern_names(pat.url_patterns)
        elif pat.__class__.__name__ == 'RegexURLPattern':           # load name from this RegexURLPattern
            if pat.name is not None:
                print '[API-URL] {} \t\t\t-> {}'.format(pat.name, pat.regex.pattern)

Then, in your urls.py:

urlpatterns = [
    url(r'^', include(router.urls)),
]

if settings.DEBUG:
    print_url_pattern_names(urlpatterns)

If you want the url to be different from your ModelViewSet´s name (as in my case) you can set it on the router with "base_name":

router.register('contents', media_views.MediaViewSet, base_name='contents')

Next thing you´ll need is extending HyperlinkedModelSerializer:

from rest_framework import serializers
# in this sample my object is of type "Media"
class MediaSerializer(serializers.HyperlinkedModelSerializer):
    #blablabla

You will have a serializer ready to show hyperlinks to your detail view, but there is one required step left for this to work. That´s where the "view_name" comes in:

from rest_framework import serializers
class MediaSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Media
        fields = ('url', ...[other fields you want to serialize])
        extra_kwargs = {
            'url': {'view_name': 'contents-detail'}
        }
  1. 'url' field is mandatory to show the link.
  2. With 'extra_kwargs' > view_name you´re telling the framework that your detail view´s name is 'content-detail'.
  3. 'content-detail' is just MY view´s name. You need to find out yours (from the url names utility above)

And this is the http response from the sample (see url field):

enter image description here

like image 117
xleon Avatar answered Oct 07 '22 15:10

xleon


Little update to xleon's answer:

With Python 3 and new Django, function to see all urlpatterns you should use something like this:

def print_url_pattern_names(patterns):
    """Print a list of urlpattern and their names"""
    for pat in patterns:
        if pat.__class__.__name__ == 'URLResolver':      # load patterns from this URLResolver
            print_url_pattern_names(pat.url_patterns)
        elif pat.__class__.__name__ == 'URLPattern':     # load name from this URLPattern
            if pat.name is not None:
                print('[API-URL] {:>50} -> {}'.format(pat.name, pat.pattern))

I spent some time resolving it, so maybe it will help somebody.

How I used it: run "python manage.py shell", then

import urls

then copy this function and run it like this:

print_url_pattern_names(urls.urlpatterns)
like image 21
Vladimir Oprya Avatar answered Oct 07 '22 16:10

Vladimir Oprya