7

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.

3 Answers 3

5

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) 
Sign up to request clarification or add additional context in comments.

8 Comments

In my case, the serializer is not valid. If I don't transmit the date field as blank, I get the Date has the wrong format. Use one of these formats instead: YYYY-MM-DD. Correspondingly, the code will not go further than serializer.is_valid().
Then you have to make your serializer valid with appropriate changes and then use the above method to change the value of serializer object before save
Then, first of all, change the date with serializer.object.date_field = required date and then after check it that is it valid or not.
That's an interesting idea. I'll try it now.
Great, Happy Coding :)
|
5

In my case I had the same problem but with validation feature I used the way below and it works for me (not excludes the way found above):

class CustomViewClass(APIView): def post(self, request, format=None): prepared_data_variable = 'some data in needed format' serializer = CustomSerializer(data=request.data) if serializer.is_valid(self): serializer.validated_data['field_name'] = prepared_data_variable serializer.save() return Response(data=serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

This string is key for my solution serializer.validated_data['field_name'] = prepared_data_variable

2 Comments

In my case, the serializer is not valid. Еhe code will not go further than serializer.is_valid(). We discussed ways to address Michael's response in his comments on
Absolutely agree with you, I just tried to solve a similar problem and I got the solution. Maybe my idea can extend your solution I thought. Only Mike's solution gave me the food for the brain.
1

To answer your question: just override save() method for your models as written in docs. There you can assign any values to your model instance directly before saving it in database.

Also, you should probably use models.EmailField for your email fields which will get rid of your lower() check.

4 Comments

I think its more suitable for pure Django not for DRF
The save method will not be called because the data will not be validated.
Ok, got it. You could either: 1. pass null instead of empty string to endpoint 2. Override data at your view, before passing it to serializer 3. Setup to_internal_value(self, data) method in your serializer In the last case - it should do smth like this: if not data['date']: data['date'] = None; return data docs
1. if you mean don't pass on nda at all, then yes, it will work. 2. the most appropriate way I think. 3. It should work, but it's harder than the second way.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.