4

When fields need to be filled programmatically in Django Rest Framework, the pre_save method may be overridden in the APIView, and the needed fields can be populated there, like:

def pre_save(self, obj):
    obj.owner = self.request.user

This works great for flat objects, but in case of nested situations, the nested object cannot be accessed in the pre_save method. The only solution I found so far is to override the save_object method, and check if the object is an instance of the nested class, and if so, populate that field there. Although this works, I don't like the solution, and would like to know if anyone found a better way?

Demonstrating the situation:

class Notebook(models.Model):
    owner = models.ForeignKey(User)

class Note(models.Model):
    owner = models.ForeignKey(User)
    notebook = models.ForeignKey(Notebook)
    note = models.TextField()

class NoteSerializer(serializers.ModelSerializer):
    owner = serializers.Field(source='owner.username')
    class Meta:
        model = Note
        fields = ('note', 'owner')

class NotebookSerializer(serializers.ModelSerializer):
    notes = NoteSerializer(many=True)
    owner = serializers.Field(source='owner.username')
    class Meta:
        model = Notebook
        fields = ('notes', 'owner')

    def save_object(self, obj, **kwargs):
        if isinstance(obj, Note):
            obj.owner = obj.notebook.owner

        return super(NotebookSerializer, self).save_object(obj, **kwargs)

class NotebookCreateAPIView(CreateAPIView):
    model = Notebook
    permission_classes = (IsAuthenticated,)
    serializer_class = NotebookSerializer

    def pre_save(self, obj):
        obj.owner = self.request.user

Before asking why don't I use different endpoints for creating notebooks and notes separately, let me say that I do that, but I also need a functionality to provide initial notes on creation of the notebook, so that's why I need this kind of endpoint as well.

Also, before I figured out this hackish solution, I actually expected that I will have to override the save_object method of the NoteSerializer class itself, but it turned out in case of nested objects, it won't even be called, only the root object's save_objects method, for all the nested objects, but I guess it was a design decision.

So once again, is this solvable in a more idiomatic way?

1 Answer 1

4

You can access the request in your serializer context.

So my approach to this would be:

class NoteSerializer(serializers.ModelSerializer):
    owner = serializers.Field(source='owner.username')

    def restore_object(self, attrs, instance=None):
        instance = super(NoteSerializer, self).restore_object(attrs, instance)
        instance.owner = self.context['request'].user
        return instance

    class Meta:
        model = Note
        fields = ('note', 'owner')

And the same on the NotebookSerializer.

The Serializer context will be made available to all used serializers in the ViewSet.

Sign up to request clarification or add additional context in comments.

1 Comment

dammit, this really looks good, I'll try this out and get back to you, thanks until then

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.