1
votes

Let's say I have this simple model :

class BlogPost(models.Model):
    author = models.ForeignKey(MyUser)
    body = models.TextField()
    title = models.CharField(max_length=64)
    urlid = models.CharField(max_length=32)
    private_data = models.CharField(max_length=64)

private_data contains data that I do not want to expose to the API (!). I'm using a ModelSerializer :

class BlogPostSerializer(serializers.ModelSerializer):
    class Meta:
        model = BlogPost

    def __init__(self, *args, **kwargs):
        # Don't pass the 'request' arg up to the superclass
        request = kwargs.pop('request', None)
        # Instatiate the superclass normally
        super(ModelSerializer, self).__init__(*args, **kwargs)
        self.request = request

    def absolute_url(self, blogpost):
        return blogpost.get_absolute_url(self.request)

The absolute_url method needs the request to determine the domain name (dev or prod for example) and if it was made in http or https.

I want to specify which fields in the model are going to get returned by the serializer (not expose private_data for example). Simple enough:

class BlogPostSerializer(serializers.ModelSerializer):
    class Meta:
        model = BlogPost
        fields = ('author', 'body', 'title', 'urlid',)

    # The same jazz after that

All right, it works. Now I also want to return absoluteUrl:

class BlogPostSerializer(serializers.ModelSerializer):
    absoluteUrl = serializers.SerializerMethodField('absolute_url')

    class Meta:
        model = BlogPost
        fields = ('author', 'body', 'title', 'urlid',)

    # The same jazz after that

Well, without surprises, this returns only the fields I specified, without the absoluteUrl. How can I return only certain fields of the model AND the absoluteUrl, calculated from the serializer?

If I don't specify fields I do get the absoluteUrl, but with all the model's fields (including private_data). If I add 'absoluteUrl' to fields I get an error because blogpost.absoluteUrl doesn't exist (no surprises there). I don't think I could use this method http://django-rest-framework.org/api-guide/serializers.html#specifying-fields-explicitly because I need the request to obtain the absoluteUrl (or can I specify arguments to the model's method ?)

1
Obviously I could move the logic from blogpost.get_absolute_url to the serializer but I'd rather leave the logic in the Model. Plus I'm not a huge fan of copy pasting functions. - Pierre Mourlanne

1 Answers

4
votes

If I don't specify fields I do get the absoluteUrl, but with all the model's fields (including private_data). If I add 'absoluteUrl' to fields I get an error because blogpost.absoluteUrl doesn't exist (no surprises there).

You should just be adding 'absoluteUrl' to the fields tuple, and it should work just fine - so what error are you seeing?

The absolute_url method needs the request to determine the domain name (dev or prod for example) and if it was made in http or https.

Note that you can also pass through context to the serializer without modfiying the __init__, just pass a context={'request': request} when instantiating the serializer. The default set of generic views do this for you, so you can access self.context['request'] in any of the serializer methods. (Note that this is how hyperlinked relationships are able to return fully qualified URLs)