Creating a IntegrityError for testing purposes
For testing purposes and building my own understanding, I'd like to create a simple Django model and form that can result in a django.db.utils.IntegrityError when saved. Ideally, the error would be null value in column "foo" of relation "bar" violates not-null constraint or similar.
I've tried creating a test model like the following:
class ATestModel(models.Model):
field = models.BooleanField(null=False)
def __str__(self):
return f'<ATestModel(field={self.field})>'
and a test admin form like this:
class ATestForm(forms.ModelForm):
field = forms.NullBooleanField(label='field')
def clean(self):
return
@admin.register(ATestModel)
class ATestAdminWithErrors(admin.ModelAdmin):
form = ATestForm
But the form still validates the model and will give an error on the page:
Please correct the error below.
“None” value must be either True or False.
How can I modify my example to generate a django.db.utils.IntegrityError when saved?
Your form will never reach the database constraint, because the None is rejected in two earlier validation layers: first the form field itself (an empty NullBooleanField produces exactly the "None" value must be either True or False error you saw), and after that a ModelForm also runs the model's full_clean() before saving. So save() is never called with None, and no query ever reaches the database.
To generate the error for testing, bypass the form and save at the model layer instead:
from django.db import transaction, IntegrityError
with transaction.atomic():
ATestModel(field=None).save() # raises IntegrityError
ATestModel.objects.create(field=None) raises it too. I verified both against a BooleanField(null=False) model: on SQLite the message is NOT NULL constraint failed: ...field, and on PostgreSQL it is the null value in column "field" ... violates not-null constraint wording you quoted. The transaction.atomic() wrapper matters in tests: an IntegrityError breaks the surrounding transaction, so inside a TestCase you want the failing save isolated in its own atomic block (or use TransactionTestCase).