Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework: when to create a hyperlinked resource and when nested resource? How to POST a nested resource?

I'm building a REST web API using the Django REST Framework. Things are going great, but I have, however stumbled upon a problem with nested resources. At first, all relationships in the REST API were hyperlinked. A post, for example, looked like this:

{
    "path": "http://api.myproject.com/posts/1.json",
    "id": 1,
    "author": "http://api.myproject.com/users/broak.json",
    "image": "/images/posts/cxyUzlPo.jpg",
    "header": "Who am I?",
    "footer": "I am a champion!",
    "date": "2014-11-09 15:16",
    "likes": "http://api.myproject.com/posts/1/likes.json",
    "comments": "http://api.myproject.com/posts/1/comments.json",
    "likes_count": 0,
    "comments_count": 0
}

The relationship between the post and the author (user) is hyperlinked. When you want to create a new post, you need to specify a correct hyperlink to a specific user - this works fine.

When calling a list of posts, things become inefficient, because you have to make an extra API call for every author for every post. I solved this by using NESTED resources instead of HYPERLINKED resources, so every post now contains all the information about the author.

{
    "path": "http://api.myproject.com/posts/1.json",
    "id": 1,
    "author": {
        "email": "[email protected]"
        "username": "broak",
        "first_name: "John",
        "last_name": "Broak",
        "is_staff": False,
        "is_active": True,
        "last_login": "02-26-2016"
    },
    "image": "/images/posts/cxyUzlPo.jpg",
    "header": "Who am I?",
    "footer": "I am a champion!",
    "date": "2014-11-09 15:16",
    "likes": "http://api.myproject.com/posts/1/likes.json",
    "comments": "http://api.myproject.com/posts/1/comments.json",
    "likes_count": 0,
    "comments_count": 0
}

My first question is: do you have a guideline, whether I should create a nested data structure or a separate endpoint with hyperlink to it.

My second question is: when I use author as a nested resource and want to create a new post, I don't want to specify all the information about the author (username, e-mail, ...). Is there any way to just use a link to a user for the CREATE/UPDATE operation? Or modify something so that the user ID is enough to fill in this field?

like image 695
Braek Avatar asked Nov 13 '14 01:11

Braek


People also ask

When should I use Django REST framework?

In order to complete your app, if you want to code more python / Django code then use Django, to the contrary if you want to code more JavaScript code then use Django restful API. For web application itself I like using Django ,because it is based on python ,it is much cleaner and shorter than js.

What is Basename in Django REST framework?

basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.


1 Answers

If i understood your question correctly, You want to have expanded author while you are retrieving the data and just want to send ID or URL in case of update and create.

1# It is not about any guideline and it totally depends on your requirement of how your api is going to be used.

2# So you need to extend your UserSerializer and override to_internal_value. Sample code might look like

class MyCustomSerializer(UserSerializer):
    def to_internal_value(self, data):
        # data must be valid user-detail url
        return serializers.HyperLinkedRelatedField(queryset=User.objects.all(), view_name='user-detail').to_internal_value(data)

Notice that you must have a Endpoint for user-detail in able to work with HyperLinkedRelatedField.

So If you want to be able to send ID then sample code might look like

class MyCustomSerializer(UserSerializer):
    # data must be valid user id
    def to_internal_value(self, data):
        return serializers.PrimaryKeyRelatedField(queryset=User.objects.all()).to_internal_value(data)

However i would like to keep consistency in sending ForeignKey field in POST/PUT/PATCH. (Always Either URL or ID).

then use it in your code like

class PostSerializer(serializers.HyperlinkedModelSerializer):
    author = MyCustomSerializer()

    class Meta:
        model = Post

Please see the documentation on Writable nested serializers to POST on a Nested resource.

like image 154
Satyajeet Avatar answered Sep 29 '22 12:09

Satyajeet