Home > Software design >  How to ensure only one entry is True in a Django model?
How to ensure only one entry is True in a Django model?

Time:06-12

I'm stuck on thinking about implementing a "only one entry might be True for one combination".

A Project has n members (Guards) through an intermediate table.

  • every Guard may be member of n Projects
  • only one combination of Guard <-> Project is allowed (unique_together)
  • a MemberShip might be the 'Main' one (is_main)
  • BUT: Only one of the memberships may be Main.

Do I oversee something or do I have to implement a custom validation on my own?

To complete this, see the given Model:

class Project(models.Model):
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    shortname = models.CharField(_('shortname'), max_length=50)
    description = models.TextField(_('description'), blank=True)
    members = models.ManyToManyField(Guard, through='ProjectMembership')

    class Meta:
        unique_together = ['client', 'shortname']


class ProjectMembership(models.Model):
    guard = models.ForeignKey(Guard, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    is_main = models.BooleanField(_('is main project'), default=False)

    class Meta:
        unique_together = ['guard', 'project']

CodePudding user response:

You can work with a UniqueConstraint [Django-doc] that is filtered:

from django.db.models import UniqueConstraint, Q

class ProjectMembership(models.Model):
    guard = models.ForeignKey(Guard, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    is_main = models.BooleanField(_('is main project'), default=False)

    class Meta:
        constraints = [
            UniqueConstraint(fields=('guard', 'project'), name='unique_guard'),
            UniqueConstraint(fields=('guard',), condition=Q(is_main=True), name='one_main_project_per_guard'),
        ]

Here we thus ensure that if we filter ProjectMembership for is_main=True, that the set of guards is unique, hence a certain Guard can only occur once for is_main, and this thus means that a Guard has at most one Project for which is_main is True.


Note: As the documentation on unique_together [Django-doc] says, the unique_together constraint will likely become deprecated. The documentation advises to use the UniqueConstraint [Django-doc] from Django's constraint framework.

  • Related