Comment définir deux champs «uniques» comme couple

389

Existe-t-il un moyen de définir quelques champs comme uniques dans Django?

J'ai un tableau des volumes (de revues) et je ne veux pas plus d'un numéro de volume pour le même journal.

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)

J'ai essayé de mettre unique = Truecomme attribut dans les champs journal_idet volume_numbermais cela ne fonctionne pas.

Giovanni Di Milia
la source

Réponses:

636

Il existe une solution simple pour vous appelée unique_together qui fait exactement ce que vous voulez.

Par exemple:

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

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

Et dans votre cas:

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
la source
2
Je dirais que vous obtiendrez une exception "ValidationError". Jetez un oeil aux documents Django: Model.validate_unique
Jens
2
Comment géreriez-vous cela si le volume_number pouvait être nul? Mysql ne semblera pas imposer unique dans ce cas.
Greg
26
Pour info, il lance une erreur django.db.utils.IntegrityError si vous essayez d'ajouter un doublon.
araneae
8
@Greg - Selon la norme ANSI SQL: 2003 (et les précédentes également), une UNIQUEcontrainte devrait interdire les non- NULLvaleurs en double , mais autoriser plusieurs NULLvaleurs (voir draft wiscorp.com/sql_2003_standard.zip , Framework, p. 22). Si vous voulez que votre contrainte unique interdise plusieurs valeurs nulles, vous faites probablement quelque chose de mal, comme utiliser NULLcomme valeur significative. N'oubliez pas que le champ nullable indique "Nous n'avons pas toujours de valeur pour ce champ mais quand nous le faisons, il doit être unique.".
2
Qu'en est-il des unique_togethercontraintes multiples ? Par exemple - quand je veux que les colonnes de mode soient uniques dans la portée du parent? Eh bien, cette propriété est en fait un tuple lui - même, voir: docs.djangoproject.com/en/1.4/ref/models/options/... Donc , votre contrainte devrait être plus explicitement écrit: unique_together = (('journal_id', 'volume_number',),).
Tomasz Gandor
78

Django 2.2+

L'utilisation des constraintsfonctionnalités UniqueConstraintest préférable à unique_together .

De la documentation Django pour unique_together:

Utilisez plutôt UniqueConstraint avec l'option de contraintes.
UniqueConstraint fournit plus de fonctionnalités que unique_together.
unique_together pourrait être obsolète à l'avenir.

Par exemple:

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
la source
Dans quelle situation le paramètre «nom» de UniqueConstraint serait-il utilisé? Je suppose que cela fonctionne comme le paramètre de nom d'un chemin URL?
user7733611
1
@ user7733611 nommer la contrainte peut être utile dans plusieurs situations. Par exemple, si vous vous connectez à une base de données héritée ou si vous souhaitez simplement que les noms de contraintes soient plus lisibles par l'homme dans la base de données. Une fois, j'ai migré le jeu de caractères d'une base de données MySQL et les noms de contraintes générés par Django étaient en fait trop longs pour notre cible particulière.
mihow
Pas sûr à 100%, UniqueConstraintmais je deviens bizarre psycopg2.errors.DuplicateTable: relation "name_of_the_constraint" already existsquand je passe à Postgres
zar3bski