DRF: how to change the value of the model fields before saving to the database

Jekson :

If I need to change some field values before saving to the database as I think models method clear() is suitable. But I can't call him despite all my efforts.

For example fields email I need set to lowercase and fields nda I need set as null

models.py

class Vendors(models.Model):

    nda = models.DateField(blank=True, null=True)
    parent = models.OneToOneField('Vendors', models.DO_NOTHING, blank=True, null=True)

    def clean(self):
        if self.nda == "":
            self.nda = None

class VendorContacts(models.Model):
    ....
    vendor = models.ForeignKey('Vendors', related_name='contacts', on_delete=models.CASCADE)
    email = models.CharField(max_length=80, blank=True, null=True, unique=True)

    def clean(self):
        if self.email:
            self.email = self.email.lower()

serializer.py

class VendorContactSerializer(serializers.ModelSerializer):
    class Meta:
        model = VendorContacts
        fields = (
                  ...
                  'email',)

class VendorsSerializer(serializers.ModelSerializer):
    contacts = VendorContactSerializer(many=True)

    class Meta:
        model = Vendors
        fields = (...
                  'nda',
                  'contacts',
                  )

    def create(self, validated_data):
        contact_data = validated_data.pop('contacts')
        vendor = Vendors.objects.create(**validated_data)
        for data in contact_data:
            VendorContacts.objects.create(vendor=vendor, **data)

        return vendor

views.py

class VendorsCreateView(APIView):
    """Create new vendor instances from form"""
    permission_classes = (permissions.AllowAny,)
    serializer_class = VendorsSerializer

    def post(self, request, *args, **kwargs):
        serializer = VendorsSerializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            serializer.save()
        except ValidationError:
            return Response({"errors": (serializer.errors,)},
                            status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(request.data, status=status.HTTP_200_OK)

As I learned from the documentation

Django Rest Framework serializers do not call the Model.clean when validating model serializers

In dealing with this problem, I found two ways to solve it. 1. using the custom method at serializer. For my case, it looks like

class VendorsSerializer(serializers.ModelSerializer): contacts = VendorContactSerializer(many=True)

class Meta:
    model = Vendors
    fields = (...
              'nda',
              'contacts',
              )

def create(self, validated_data):
    contact_data = validated_data.pop('contacts')
    vendor = Vendors.objects.create(**validated_data)
    for data in contact_data:
        VendorContacts.objects.create(vendor=vendor, **data)

    return vendor

def validate(self, attrs):
    instance = Vendors(**attrs)
    instance.clean()
    return attrs
  1. Using full_clean() method. For me, it looks like

class VendorsSerializer(serializers.ModelSerializer): contacts = VendorContactSerializer(many=True)

class Meta:
    model = Vendors
    fields = (...
              'nda',
              'contacts',
              )

def create(self, validated_data):
    contact_data = validated_data.pop('contacts')
    vendor = Vendors(**validated_data)
    vendor.full_clean()
    vendor.save()
    for data in contact_data:
        VendorContacts.objects.create(vendor=vendor, **data)

    return vendor

But in both cases, the clean() method is not called. I really don't understand what I'm doing wrong.

MK Patel :

For DRF you can change your serializer before save as below...

First of all, you should check that serializer is valid or not, and if it is valid then change the required object of the serializer and then save that serializer.

if serializer.is_valid():
    serializer.object.user_id = 15  # For example 
    serializer.save() 

UPD! views.py

class VendorsCreateView(APIView):
    """Create new vendor instances from form"""
    permission_classes = (permissions.AllowAny,)
    serializer_class = VendorsSerializer

    def post(self, request, *args, **kwargs):
        data = request.data
        if data['nda'] == '':
            data['nda'] = None
        for contact in data['contacts']:
            if contact['email']:
                print(contact['email'])
                contact['email'] = contact['email'].lower()
        serializer = VendorsSerializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            serializer.save()
        except ValidationError:
            return Response({"errors": (serializer.errors,)},
                            status=status.HTTP_400_BAD_REQUEST)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=350593&siteId=1
Recommended