1

I have a Model class with a series of constraints that I am attempting to test, and I am unable to get these constraints to return an IntegrityError in testing. The class is as follows:

from django.db import models
from django.db.models import CheckConstraint, Q, UniqueConstraint


class Products(models.Model):
    sku = models.CharField(primary_key=True, max_length=8)
    barcode = models.CharField(unique=True, max_length=14, blank=True, null=True)
    name = models.TextField(blank=True, null=True)
    rrp = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    status = models.TextField()
    manufacturer = models.TextField(blank=True, null=True)
    brand = models.TextField(blank=True, null=True)
    country_of_origin = models.TextField(blank=True, null=True)
    last_updated = models.DateField(blank=True, null=True)
    date_published = models.DateField(blank=True, null=True)
    exclusive = models.BooleanField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'product'
        constraints = [
            CheckConstraint(
                condition=Q(status__in=['Draft', 'Live', 'Discontinued']),
                name='check_status',
                violation_error_message="status field must be on of the following: 'Draft', 'Live', 'Discontinued'",
            ),
            CheckConstraint(
                condition=~(Q(date_published__isnull=False) & Q(status__in=['Draft', 'Discontinued'])),
                name='check_date_published',
                violation_error_message="Product with status 'Draft' or 'Discontinued' cannot have a date_published value"
            ),
            UniqueConstraint(fields=['barcode'], name='unique_barcode'),
            UniqueConstraint(fields=['sku', 'exclusive'], name='unique_sku_and_exclusive'),
            UniqueConstraint(fields=['sku', 'status'], name='unique_sku_and_status')
        ]

The 'managed' value is flipped in 0001_initial.py when tests are run. The IntegrityError I'm using is from django.db.utils and not sqlite3, however the save() method isn't returning any exceptions to begin with, so the issue is not coming from the wrong version of IntegrityError.

from django.db.utils import IntegrityError
from django.test import TestCase

from .models import Products


class ProductTestCase(TestCase):
    def setUp(self):
        self.product = Products.objects.create(
            sku = '12345678',
            barcode = '5666777888999',
            name = 'Test Product 1',
            rrp = 999,
            status = 'Live',
            manufacturer = '',
            brand = 'Test Brand',
            country_of_origin = 'China',
            last_updated = '2025-01-01',
            date_published = '2025-01-01',
            exclusive = False
    )

    def test_invalid_product_status_not_null(self):
        """Test product can't be made with an invalid status value"""
        self.product.status = 'WrongStatus'
        with self.assertRaises(IntegrityError):
            self.product.save()

I have attempted simply creating a Products object in the with clause with only sku and status, I've attempted having the constraints both inside and outside of the constraints list in the meta, but I just can't get the CheckConstraint to trigger. Am I misunderstanding when constraints are supposed to trigger? Or am I writing the Q objects incorrectly?

5
  • What database are you using? Not all databases run CHECKs iirc. Commented Sep 8 at 6:27
  • I'm using PostgreSQL 14.18 Commented Sep 8 at 7:46
  • What Django version are you using? Commented Sep 8 at 7:47
  • It's Django 5.2.2 Commented Sep 8 at 7:58
  • It says managed = False, so that means Django will not add any constraints on the database? Commented Sep 8 at 8:55

1 Answer 1

0

The constraints are enforced by the database, so Django translates a CheckConstraint into a CHECK status IN ('Draft', 'Live', 'Discontinued').

But you also said managed = False [Django-doc], this means Django is not supposed to make changes to the structure of the database table. So it will not add/remove fields/constraints/…

As a result, adding items to constraints will indeed not matter, you will have to manually add these to the database.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. So given that constraints are for defining the migrations, they (the ones defined in Django and not the database) presumably won't trigger outside of testing either? Is it essentially pointless to write them in Django in this case?
If you use managed = False, yes, since then Django does not sync the database table with the model.

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.