Как сделать так, чтобы только одна запись была True в модели Django?

Я застрял на мысли о реализации "только одна запись может быть True для одной комбинации".

Проект имеет n членов (охранников) через промежуточную таблицу.

  • каждый Страж может быть членом n Проектов
  • допускается только одна комбинация Guard <-> Project (unique_together)
  • членский корабль может быть "главным" (is_main)
  • BUT: Только одно из членств может быть главным.
  • .

Я что-то упустил или мне нужно самостоятельно реализовать пользовательскую валидацию?

Чтобы завершить это, смотрите данную модель:

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']

Вы можете работать с UniqueConstraint [Django-doc], который фильтруется:

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'),
        ]

Здесь мы гарантируем, что если мы фильтруем ProjectMembership для is_main=True, то набор guard уникален, следовательно, определенный Guard может встречаться только один раз для is_main, и это означает, что Guard имеет максимум один Project, для которого is_main является 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.

Вернуться на верх