Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework – Custom Hyperlink field in serializer

How can I add a custom hyperlink field in a serializer? I would like to have a hyperlink field in my serializer that has query params in it. Since there is no way to pass query params from HyperlinkedRelatedField or HyperlinkedIdentityField as far as I know, I've tried using a SerializerMethodField. However, this only serializes to a string, and is not a clickable URL when I visit the API through my browser. My code looks something like this:

class MySerializer(serializers.HyperlinkedModelSerializer):
    custom_field = serializers.SerializerMethodField()

    class Meta:
        model = MyModel
        fields = ('url', 'custom_field')

    def get_custom_field(self, obj):
        result = '{}?{}'.format(
            reverse('my-view'),
            urllib.urlencode({'param': 'foo'})
        )
        return result

Also, I am having trouble understanding the difference between a HyperlinkedRelatedField and a HyperlinkedIdentityField, so a brief explanation would be appreciated.

like image 603
b_pcakes Avatar asked Feb 22 '16 06:02

b_pcakes


1 Answers

This should do the trick:

from rest_framework.reverse import reverse

class MySerializer(serializers.HyperlinkedModelSerializer):
    custom_field = serializers.SerializerMethodField()

    class Meta:
        model = MyModel
        fields = ('url', 'custom_field')

    def get_custom_field(self, obj):
        result = '{}?{}'.format(
            reverse('my-view', args=[obj.id], request=self.context['request']),
            'param=foo'
        )
        return result

The reverse function in rest_framework takes a view name (whatever view you'd like to link to), either an args list (the object id, in this case) or kwargs, and a request object (which can be accessed inside the serializer at self.context['request']). It can additionally take a format parameter and any extra parameters (as a dictionary) that you want to pass to it.

The reverse function then builds a nice, fully-formed URL for you. You can add query params to it by simply adding as many ?{}&{}&{} to your result variable and then filling in the series of query params beneath the 'param=foo' inside your format function with whatever other params you want.

The HyperlinkedIdentityField is used on the object itself that is being serialized. So a HyperlinkedIdentifyField is being used in place of your primary key field on MyModel because you are using a HyperlinkedModelSerializer which creates a HyperlinkedIdentityField for the pk of the object itself being serialized.

The HyperlinkedRelatedField is used to define hyperlinked relationships to RELATED objects. So if there were a MySecondModel with a foreign key relationship to MyModel and you wanted to have a hyperlink on your MyModel serializer to all the related MySecondModel objects you would use a HyperlinkedRelatedField like so (remember to add the new field to your fields attribute in Meta):

class MySerializer(serializers.HyperlinkedModelSerializer):
    custom_field = serializers.SerializerMethodField()
    mysecondmodels = serializers.HyperlinkedRelatedField(
        many=True
        read_only=True,
        view_name='mysecondmodel-detail'
    )

    class Meta:
        model = MyModel
        fields = ('url', 'custom_field', 'mysecondmodels')

    def get_custom_field(self, obj):
        result = '{}?{}'.format(
            reverse('my-view', args=[obj.id], request=self.context['request']),
            'param=foo'
        )
        return result

If it were a OneToOneField rather than ForeignKey field on MySecondModel then you would set many=False.

Hope this helps!

like image 164
user1847 Avatar answered Oct 05 '22 04:10

user1847