Como definir dois campos "únicos" como casal

388

Existe uma maneira de definir alguns campos como únicos no Django?

Tenho uma tabela de volumes (de diários) e não quero mais do que um número de volume para o mesmo diário.

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

Eu tentei colocar unique = Truecomo atributo nos campos journal_ide volume_numbermas não funciona.

Giovanni Di Milia
fonte

Respostas:

634

Existe uma solução simples para você chamada unique_together que faz exatamente o que você deseja.

Por exemplo:

class MyModel(models.Model):
  field1 = models.CharField(max_length=50)
  field2 = models.CharField(max_length=50)

  class Meta:
    unique_together = ('field1', 'field2',)

E no seu caso:

class Volume(models.Model):
  id = models.AutoField(primary_key=True)
  journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
  volume_number = models.CharField('Volume Number', max_length=100)
  comments = models.TextField('Comments', max_length=4000, blank=True)

  class Meta:
    unique_together = ('journal_id', 'volume_number',)
Jens
fonte
2
Eu diria que você receberá uma exceção "ValidationError". Dê uma olhada nos documentos do Django: Model.validate_unique
Jens
2
Como você lidaria com isso, se volume_number pudesse ser nulo? O Mysql não parecerá forçar exclusivo nesse caso.
Greg
26
Para sua informação, lança um django.db.utils.IntegrityError se você tentar adicionar uma duplicata.
precisa saber é
8
@Greg - De acordo com o padrão ANSI SQL: 2003 (e também os anteriores), uma UNIQUErestrição deve proibir não- NULLvalores duplicados , mas permitir vários NULLvalores (consulte o rascunho wiscorp.com/sql_2003_standard.zip , Framework, p. 22). Se você deseja que sua restrição exclusiva não permita vários valores nulos, provavelmente está fazendo algo errado, como usar NULLcomo um valor significativo. Lembre-se, o campo anulável diz "Nem sempre temos um valor para esse campo, mas quando o fazemos, ele deve ser único".
2
E as múltiplas unique_togetherrestrições? Por exemplo - quando eu quero que as colunas de modo sejam exclusivas no escopo do pai? Bem, esta propriedade é realmente uma própria tupla, consulte: docs.djangoproject.com/en/1.4/ref/models/options/... Portanto, sua restrição deve ser mais explicitamente escrito como: unique_together = (('journal_id', 'volume_number',),).
Tomasz Gandor
76

Django 2.2+

O uso dos constraintsrecursos UniqueConstrainté preferível a unique_together .

Na documentação do Django para unique_together:

Use UniqueConstraint com a opção de restrições.
UniqueConstraint fornece mais funcionalidade que unique_together.
unique_together pode ser preterido no futuro.

Por exemplo:

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name="Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['journal_id', 'volume_number'], name='name of constraint')
        ]
daaawx
fonte
Em que situação o parâmetro 'name' da UniqueConstraint seria usado? Presumo que funciona como o parâmetro name de um caminho de URL?
User7733611
11
@ user7733611 nomear a restrição pode ser útil em várias situações. Por exemplo, se você estiver se conectando a um banco de dados herdado ou apenas desejar que os nomes das restrições sejam mais legíveis por humanos no banco de dados. Uma vez, migrei o conjunto de caracteres de um banco de dados MySQL e os nomes de restrições gerados pelo Django eram realmente muito longos para o nosso alvo em particular.
mihow
Não 100% de certeza que vem UniqueConstraint, mas eu recebo estranho psycopg2.errors.DuplicateTable: relation "name_of_the_constraint" already existsquando eu mudar para Postgres
zar3bski