Overriding create() for class based generic views

Question:

How can I perform some logic prior to saving a record within my generic view? I believe the actual saving logic occurs within super().create().

The request within create() looks like this

<QueryDict: {'csrfmiddlewaretoken': ['5WvMZnoBMCUjmlMBaacLnx6Pxt3jUDvHWHvo90ORumYrClkebcx7NJZpmWASRIyG'], 'user': ['1'], 'address': ['3E8ociqZa9mZUSwGdSmAEMAoAxBK3FNDcd']}>

view

class WalletListCreateAPIView(generics.ListCreateAPIView):
    queryset = Wallet.objects.all()
    serializer_class = WalletSerializer

    def create(self, request, *args, **kwargs):
        # Some logic here prior to saving
        return super().create(request, *args, **kwargs)

For instance, I would like to create the value for balance instead of relying on the value from the request

class Wallet(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    address = models.CharField(max_length=34)
    balance = models.DecimalField(default=0, max_digits=16, decimal_places=8)
    slug = models.SlugField(max_length=34, blank=True, null=True)
Asked By: Liondancer

||

Answers:

I understand your problem.

you want to override create method in generics.ListCreateAPIView.

so you have to define some method, here you want to create new record so you want to define post method and in post method you can override create method.

class WalletListCreateAPIView(generics.ListCreateAPIView):
   queryset = Wallet.objects.all()
   serializer_class = WalletSerializer

   def post(self, request, *args, **kwargs):
      return self.create(request, *args, **kwargs)

NOTE:
– as per your Wallet model address field is not blank, so you should have pass address. then run it.

Answered By: Hardik Sathavara

This is the flow when you are saving your request data into model
Also please check the syntax
create -> perform_create -> serializer’s create back to perform create then back to create

class WalletListCreateAPIView(generics.ListCreateAPIView):
    queryset = Wallet.objects.all()
    serializer_class = WalletSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        wallet = self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        return Response(
            self.get_response_data(user),
            status=status.HTTP_201_CREATED,
            headers=headers,
        )


    def perform_create(self, serializer):
        wallet = serializer.save(user=self.request.user) # if you want to change how you want to save from serializer to your model then you should override create method of serializer as I have shown below
        wallet.balance = 30
        wallet.save()
        return wallet


# serializers.py
class WalletSerializer(serializers.ModelSerializer):
    class Meta:
        model = Wallet
        fields = "__all__"
    def create(self, validated_data):
        # here in validated data you will receive your request data after validation  If you want to discard any request value you can do here
        balance = validated_data.pop("balance", None)
        wallet = Wallet.objects.create(**validated_data)
        return wallet
Answered By: Deepak Tripathi

You can override model save method

class Wallet(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    address = models.CharField(max_length=34)
    balance = models.DecimalField(default=0, max_digits=16, decimal_places=8)
    slug = models.SlugField(max_length=34, blank=True, null=True)
    
    def save(self, *args, **kwargs):
      if not self.pk:
         #You can write own logic here
         self.balance = 10 
       super(Wallet, self).save(*args, **kwargs)


Answered By: Krishna Singhal