Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

best way to add additional fields to django-rest-framework ModelViewSet when create

I have a Book model with a foreign key to user (the owner of the book):

class Book(models.Model):
    owner = models.ForiegnKey(User)
    ...

I've created a ModelViewSet for Book which shows the books owned by the logged in user:

class BookViewSet(viewsets.ModelViewSet):
    model = Book
    serializer_class = BookSerializer
    def get_queryset(self):
        return Book.objects.filter(owner=self.request.user)

Now to create a new book, I want to save user field with request.user, not with data sent from the rest client (for more security). for example:

def create(self, request, *args, **kwargs):
    request.DATA['user'] = request.user
    ... (some code to create new Book from request.DATA using serialize class)

but I got this error: This QueryDict instance is immutable. (means request.DATA is a immutable QueryDict and can't be changed)

Do you know any better way to add additional fields when creating an object with django rest framework?

like image 669
Ali Avatar asked Sep 02 '13 10:09

Ali


3 Answers

Update: Since v3 you need to do this:

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

The principle remains the same.


You want to make the owner field of your book serializer read-only and then set the association with the user in pre_save().

Something like:

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

See the tutorial section on "Associating Snippets with Users".

I hope that helps.

like image 112
Carlton Gibson Avatar answered Oct 23 '22 10:10

Carlton Gibson


For what it's worth this has changed in Django REST Framework 3. There is now a perform_create() that replaces the older pre_save() and post_save() hooks suggested in a previous answer. For example:

from rest_framework import viewsets

class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

Hope this saves someone some time.

like image 23
Paul J Avatar answered Oct 23 '22 11:10

Paul J


If you're using ModelSerializer it's as easy as implementing the restore_object() method:

class MySerializer(serializers.ModelSerializer):

    def restore_object(self, attrs, instance=None):

        create = not instance
        instance = super(MySerializer, self).restore_object(attrs, instance)

        if create:
            instance.user = self.context['request'].user

        return instance

    # ...

restore_object() is used to deserialize a dictionary of attributes into an object instance. ModelSerializer implements this method and creates/updates the instance for the model you specified in the Meta class. If the given instance is None it means the object still has to be created. In this case you just set the user field with the desired value.

More information: http://www.django-rest-framework.org/api-guide/serializers#declaring-serializers

like image 1
gitaarik Avatar answered Oct 23 '22 10:10

gitaarik