Django - If I get an error while saving a model instance, how do I check which field caused the error?

django form fields
django model save
django model form
django model fields
django form widgets
django get form data
django inline formset
django modelform foreign key

Let's say I have the following model MyModel (it is just an example; the real version has hundreds of fields):

class MyModel(models.Model):
    date1 = models.DateField()
    date2 = models.DateField()
    date3 = models.DateField()

When I am saving the model, I get the following error:

ValidationError: ["'' value has an invalid date format. It must be in YYYY-MM-DD format."]

Basically, one of the fields received an empty string as opposed to a proper date format. However, I don't know whether date1, date2, or date3 is causing the problem.

Is there some easy way to check which field returned this ValidationError?

You can validate your model fields directly by over-riding save() like this, which will also give field names for which validation failed:

class MyModel(models.Model):
  date1 = models.DateField()
  date2 = models.DateField()
  date3 = models.DateField()

  def save(self, *args, **kwargs):
    self.full_clean()
    super(MyModel, self).save(*args, **kwargs)

However you should avoid this, as explained here.

You can also write a serializer which can be used for other complex validations as well if any in your original model. For doing it via serializer you can do like this:

Create your own validator:

class DateFormatValidator(object):

    def __init__(self, field_name=""):
        self.field_name = field_name

    def __call__(self, value):
        try:
            datetime.datetime.strptime(value, '%Y-%m-%d')
        except ValueError:
            raise serializers.ValidationError("Incorrect data format for {} field, should be YYYY-MM-DD"
                                              .format(self.field_name))

and then use this for every field inside your serializer class, for ex:

date1 = serializers.CharField(validators=[DateFormatValidator(field_name='date1')])

Django, show ValidationError in template, GET and POST are the only HTTP methods to use when dealing with forms. (A ModelForm maps a model class's fields to HTML form <input> elements via a Form data from a saved model instance (as in the case of admin forms for editing) putting error messages “next to” the field could cause confusion for your users  Model.save() no longer attempts to find a row when saving a new Model instance and a default value for the primary key is provided, and always executes an INSERT. Forcing an INSERT or UPDATE ¶ In some rare circumstances, it’s necessary to be able to force the save() method to perform an SQL INSERT and not fall back to doing an UPDATE .

simple solution: log the data if errors occurs when saving.

class MyModel(models.Model):
     ...

     def save(self, *args, **kwargs):
          try:
               super(MyModel, self).save(*args, **kwargs)
          except Exception as e:
               log.error(self.date1, self.date2, self.date3)
               raise e

I also recommend you to use form in django or Serializer class in django-rest-framework to check the data before saving.

Form and field validation | Django documentation, For instance, you might have a BlogComment model, and you want to create a The generated Form class will have a form field for every model field specified, in Error messages defined on model fields are only used when the Note that if the form hasn't been validated, calling save() will do so by checking form.errors . def serializable_value (self, field_name): """ Return the value of the field name for this instance. If the field is a foreign key, return the id value instead of the object. If there's no Field object with this name on the model, return the model attribute's value. Used to serialize a field's value (in the serializer, or form output, for example).

Since all three fields are of the same type and are all expecting the same date format, and the fact that validation will begin from top to bottom, it is most likely that the error is caused by the first field.

You can fix it by setting the date input format variable in the settings module with your preferred format. eg

DATE_INPUT_FORMATS = ['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y']

Here is reference to more supported formats Django project

Working with forms | Django documentation, _meta.fields: raise TypeError( "Abstract base class containing model fields not _meta.abstract: # Check for clashes between locally declared fields and those field if it's not already present; # e.g. migrations will already have it specified if not to a model instance not # saved to the database (or auto-generated in a case  # # A Field class can implement the get_internal_type() method to specify # which *preexisting* Django Field class it's most similar to -- i.e., # a custom field might be represented by a TEXT column type, which is # the same as the TextField Django field type, which means the custom # field's get_internal_type() returns 'TextField'.

Creating forms from models | Django documentation, To create a new instance of a model, instantiate it like any other Python class: If all of the model's fields are present, then values are guaranteed to be in the We could update the previous example to assign the error to the pub_date field: You may have noticed Django database objects use the same save() method for​  GET and POST ¶. GET and POST are the only HTTP methods to use when dealing with forms.. Django’s login form is returned using the POST method, in which the browser bundles up the form data, encodes it for transmission, sends it to the server, and then receives back its response.

django.db.models.base | Django documentation, Django uses the field class types to determine a few things: Given a model instance, the display value for a field with choices can be If you change the value of the primary key on an existing object and then save it, A field name cannot be a Python reserved word, because that would result in a Python syntax error. I assume you cannot name a django field like transition model attribute. So your suggested workaround should be okay. If you want to initialize a model with the last state one could think of Machine(self, states=States, transitions=Transitions, initial=self.current_state). current_state would be a field as mentioned in your workaround.

Model instance reference | Django documentation, We'll try to improve Django's error handling to catch such mistakes in the future. You've got a form that does a POST to some view, and the view doesn't If you have a list in your session, append operations don't get saved to the object. A model field is an instance of the appropriate Field class, so the  Django community: Django Q&A RSS This page, updated regularly, aggregates Django Q&A from the Django community. Retrieve and update field of a model on views and display updated field in template Posted on April 11, 2020 at 4:42 PM by Stack Overflow RSS

Comments
  • Where are you getting this error? Is it a form or do you call .full_clean() manually? Normally the ValidationError includes a key of the field that caused the problem.