0

I need to check the model field for a list of available items before I save them to the database.

models.py

class Vendors(models.Model):
    COUNTRY_CHOICES = tuple(countries)
    ...
    country = models.CharField(max_length=45, choices=COUNTRY_CHOICES)
    ...

class for saving models

class CsvToDatabase(APIView):

    def post(self, request, format=None):
        data = request.data
        for key, vendor in data.items():
            Vendors(
                ...,
                country=vendor['Country'],
                ...,

            ).save()

        return Response({'received data': request.data,
                         'message': 'Vendors from vendors_list were successfully added to the database'})

For validation Im added clean method to models

def clean(self):
    if self.country not in [x[1] for x in countries]:
        raise ValidationError(detail="Country name does not match to the country list ")

But it doesnt work

Next step I added the same code to method save

def save(self):
    if self.country not in [x[1] for x in countries]:
        raise ValidationError(detail="Country name does not match to the country list ")

And it works, but I read that using validation in the save method is not correct. And the correct method for use - clean, why in my case it does not work?

1
  • 1
    The correct way is indeed to use clean(), however the clean() method on a model isn't called automatically. It gets called when you clean a ModelForm for the model (by checking form.is_valid()) or when you validate a ModelSerializer. You can also just call it yourself. Create the Vendors object, call full_clean() first (and catch the ValidationError) and only after that call save(). Note: if you call full_clean(), the model will already validate that the country is set correctly, you don't need to add that in clean(). Commented Feb 19, 2020 at 12:39

1 Answer 1

1

See model validation for a full explanation of how validating models works.

In short: To validate an object, you need to call full_clean() on the object. This does not happen automatically! It happens when:

  • You use a ModelForm and call is_valid() on the form
  • You use a ModelSerializer in DRF and call is_valid() on the serializer
  • You call full_clean() explicitly

In your case, just create the object, call full_clean() and then save(). Note you don't even have to add your own check (clean() method) because the check for valid choices is automatically performed by Django during clean_fields() when choices is set on a field:

v = Vendors(country=vendor['country'], ...)
try:
    v.full_clean()
except ValidationError as e:
    # do something e.g. return error response
v.save()
return Response(...)
Sign up to request clarification or add additional context in comments.

2 Comments

Great, it works and it's very useful information for me. One more thing for me - how to catch the exceptions ? Now I have an exception from the clean model method, and I would like to use an exception from the POST class method. Im created a new question for this. I'd appreciate your help.
Notice: ModelSerializer.is_valid doesn't actually call Model.full_clean

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.