3

I have a model like so:

class GiveAbsolute(serializers.Field):
    def to_native(self,value): 
         # this where it give an error (self doesn't have request)
         # what i want it to give full url 
         # like: http://www.blabla.com/othermodel/1
         return reverse('link_to_othermodel',
                         args=[value],
                         request=self.request)

class SomethingSerializer(serializers.ModelSerializer):
    # field with foreign key
    othermodel = GiveAbsolute(source="othermodel.id")
    class Meta:
        model=Something
        fields("fields1","othermodel")

is there a way to achieve this ? thanks

3 Answers 3

8

From the source

The request object is an entry of the context dictionary. ie.

request = self.context.get('request')

In your case, just do:

self.request = self.context.get('request')

then build the url

self.request.build_absolute_uri(reverse('some_url_name'))
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, this post helped me in taking host name in serializer create method as it only provides self and validated_data in params.
4

Based on the answer of mariodev, here is a reusable solution for Models ; I use it to provide URLs to service (see them as metheds) on django models.

Reusable components

serializers.py

class RequestAwareSerializer(serializers.ModelSerializer):
    """
    A serializer which fields can access the request object.
    """
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(RequestAwareSerializer, self).__init__(*args, **kwargs)

class APIMethodField(serializers.Field):
    """ To get the absolute URL of a method accessible via the API
    """
    def __init__(self, url_action_name, *args, **kwargs):
        self._url_name = url_action_name
        super(APIMethodField, self).__init__(source='*', *args, **kwargs)

    def to_native(self, obj):
        """
        @param objid the ID of the object
        @param method_url_name, the name of the url, as in urls.py
        """
        return reverse_lazy(self._url_name, args=[obj.id],


                   request=self.parent.request)

views.py

class ChattyModelViewSet(ModelViewSet):
    """ ModelViewSet which informs the serializer about the request

    (abstract)
    """
    def get_serializer(self, instance=None, data=None,
                   files=None, many=False, partial=False):

        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        return serializer_class(instance, data=data, files=files, many=many,
                                partial=partial, context=context,
                                request=self.request)

Example use

urls.py

url(r'^v1/maildomain/(?P<maildomain_id>\d+)/check/$',
    views.MailDomainDetail.as_view(), name='maildomain_dns_check')

serializers.py

class MailDomainSerializer(RequestAwareSerializer):
    checkdns_url = APIMethodField(url_action_name='maildomain_dns_check')

    class Meta:
        model = MailDomain()
        fields = ('name', 'checkdns_url')

views.py

class MailDomainView(ChattyModelViewSet):
    model = MailDomain
    serializer_class = MailDomainSerializer

1 Comment

In Python 3, I used to_representation instead of to_native. without request
3

The only thing in DRF, that has an access to request object is the view, so you need to figure out how to pass your request from view to serializer, for example in generic ListView you can use get_serializer.

Then, when you already have it in your serializer, you can use self.parent (which is a parent serializer) to capture it from the field itself:

class GiveAbsolute(serializers.Field):
    def to_native(self,value):
        return reverse('link_to_othermodel',
                     args=[value],
                     request=self.parent.request)


class SomethingSerializer(serializers.ModelSerializer):
    # field with foreign key
    othermodel = GiveAbsolute(source="othermodel.id")
    class Meta:
        model=Something
        fields=("fields1","othermodel")

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(SomethingSerializer, self).__init__(*args, **kwargs)


class SomethingView(generics.ListAPIView):
    model = Something
    serializer_class = SomethingSerializer

    def get_serializer(self, instance=None, data=None,
                   files=None, many=False, partial=False):

        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        return serializer_class(instance, data=data, files=files, many=many,
                                partial=partial, context=context, request=self.request)

3 Comments

somehow i got error 'module' object is not callable. when i use pdb.set_trace() , GiveAbsolute get called first before get_serializer thus self.parent.request give None , any solution for this ? thanx
@whale_steward Can you please provide full stacktrace? also try using reverse_lazy instead of reverse..
thank you for pointing out to use reverse_lazy now is working perfectly

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.