Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Django CheckConstraint failing

I’m creating a custom user-model where every user needs a company assigned, unless they are a superuser or staff.

To accomplish this, I’m adding 3 CheckConstrains as seen below:

class CustomUser(AbstractUser):
    '''
    Custom User class, username and email are required. 
    Company is required except for staff and super users
    '''
    company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True, blank=True)

    class Meta(AbstractUser.Meta):
        constraints = [
            CheckConstraint(
                check=Q(is_superuser=False, is_staff=False, company__isnull=False),
                name="user_must_have_company",
            ),
            CheckConstraint(
                check=Q(is_staff=True, company__isnull=True),
                name="staff_cannot_have_company",
            ),
            CheckConstraint(
                check=Q(is_superuser=True, company__isnull=True),
                name="superuser_cannot_have_company",
            ),
        ]

Desired behaviour is:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  • Super-users and Staff users cannot have a company
  • all ‘normal’ users need to have a company set.

However, all initial conditions fail on the same CheckConstraint user_must_have_company.

user = CustomUser.objects.create(username='staffuser', is_staff=True, company=None)
IntegrityError: CHECK constraint failed: user_must_have_company
company = Company.objects.create(name='Test')
user = CustomUser.objects.create(username='normal_user', is_staff=True, company=company)
IntegrityError: CHECK constraint failed: user_must_have_company
user = CustomUser.objects.create(username='superuser', is_superuser=True, company=None)
IntegrityError: CHECK constraint failed: user_must_have_company

What am I missing?

>Solution :

Your checks here say that a user needs to have is_superuser=False, and is_staff=False, and company should not be None/NULL, so this is not a conditional statement, it implies that the is_superuser should always be False. The next check contradicts this by saying that is_staff should be True and company should be None/NULL, and finally the last one says that is_superuser should be True and company should be None/NULL.

What you can do is to work with the disjunctive form of an implication. Indeed, in boolean logic, the expression A → B is equivalent to ¬A ∨ B, so the condition (A) does not hold, or if it holds, its implication (B) should hold. This means that the checks are equivalent to:

class CustomUser(AbstractUser):
    # …

    class Meta(AbstractUser.Meta):
        constraints = [
            CheckConstraint(
                check=Q(is_superuser=True)
                | Q(is_staff=True)
                | Q(company__isnull=False),
                name='user_must_have_company',
            ),
            CheckConstraint(
                check=Q(is_staff=False) | Q(company=None),
                name="staff_cannot_have_company",
            ),
            CheckConstraint(
                check=Q(is_superuser=False) | Q(company=None),
                name="superuser_cannot_have_company",
            ),
        ]

In this particular case however, we can simplify this to:

class CustomUser(AbstractUser):
    # …

    class Meta(AbstractUser.Meta):
        constraints = [
            CheckConstraint(
                check=Q(is_superuser=False, is_staff=False) ^ Q(company=None),
                name='user_has_company_iff_no_staff_admin',
            ),
        ]
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading