Reputation: 6796
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?
Upvotes: 4
Views: 2807
Reputation: 4182
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
.
Upvotes: 4