'This QueryDict instance is immutable

I have a Branch model with a foreign key to account (the owner of the branch):

class Branch(SafeDeleteModel):
    _safedelete_policy = SOFT_DELETE_CASCADE
    name = models.CharField(max_length=100)
    account = models.ForeignKey(Account, null=True, on_delete=models.CASCADE)
    location = models.TextField()
    phone = models.CharField(max_length=20, blank=True,
                         null=True, default=None)
    create_at = models.DateTimeField(auto_now_add=True, null=True)
    update_at = models.DateTimeField(auto_now=True, null=True)

    def __str__(self):
        return self.name

    class Meta:
        unique_together = (('name','account'),)

    ...

I have a Account model with a foreign key to user (one to one field):

class Account(models.Model):
    _safedelete_policy = SOFT_DELETE_CASCADE
    name = models.CharField(max_length=100)
    user = models.OneToOneField(User)
    create_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name + ' - ' + self.create_at.strftime('%Y-%m-%d %H:%M:%S')

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

class BranchViewSet(viewsets.ModelViewSet):
    serializer_class = BranchSerializer
    permission_classes = (permissions.IsAuthenticated,)


    def get_queryset(self):
        queryset = Branch.objects.all().filter(account=self.request.user.account)
        return queryset

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

def create(self, request, *args, **kwargs):
    if request.user.user_type == User.ADMIN:
        request.data['account'] = request.user.account
        return super(BranchViewSet, self).create(request, *args, **kwargs)

def perform_create(self, serializer):
    '''
        Associate branch with account
    '''
    serializer.save(account=self.request.user.account)

In branch serializer

class BranchSerializer(serializers.ModelSerializer):
    account = serializers.CharField(source='account.id', read_only=True)

    class Meta:
        model = Branch
        fields = ('id', 'name', 'branch_alias',
              'location', 'phone', 'account')
        validators = [
            UniqueTogetherValidator(
                queryset=Branch.objects.all(),
                fields=('name', 'account')
            )
        ]

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?



Solution 1:[1]

As you can see in the Django documentation:

The QueryDicts at request.POST and request.GET will be immutable when accessed in a normal request/response cycle.

so you can use the recommendation from the same documentation:

To get a mutable version you need to use QueryDict.copy()

or ... use a little trick, for example, if you need to keep a reference to an object for some reason or leave the object the same:

# remember old state
_mutable = data._mutable

# set to mutable
data._mutable = True

# ?hange the values you want
data['param_name'] = 'new value'

# set mutable flag back
data._mutable = _mutable

where data it is your QueryDicts

Solution 2:[2]

Do Simple:

#views.py
from rest_framework import generics


class Login(generics.CreateAPIView):
    serializer_class = MySerializerClass
    def create(self, request, *args, **kwargs):
        request.data._mutable = True
        request.data['username'] = "[email protected]"
        request.data._mutable = False

#serializes.py
from rest_framework import serializers


class MySerializerClass(serializers.Serializer):
    username = serializers.CharField(required=False)
    password = serializers.CharField(required=False)
    class Meta:
        fields = ('username', 'password')

Solution 3:[3]

I personally think it would be more elegant to write code like this.

def create(self, request, *args, **kwargs):
    data = OrderedDict()
    data.update(request.data)
    data['account'] = request.user.account
    serializer = self.get_serializer(data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Solution 4:[4]

request.data._mutable=True

Make mutable true to enable editing in querydict or the request.

Solution 5:[5]

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

The official way to provide extra data when creating/updating an object is to pass them to the serializer.save() as shown here

Solution 6:[6]

https://docs.djangoproject.com/en/2.0/ref/request-response/#querydict-objects

The QueryDicts at request.POST and request.GET will be immutable when accessed in a normal request/response cycle. To get a mutable version you need to use QueryDict.copy().

Solution 7:[7]

You can use request=request.copy() at the first line of your function.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ?????? ?????
Solution 2 CPMaurya
Solution 3 zmaplex
Solution 4 Antoine
Solution 5 Linovia
Solution 6 kevin wong
Solution 7 Shreyash Srivsatava