Unique BooleanField valeur dans Django?
Suppose que mon models.py c'est comme si:
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
Je ne veux que l'un de mes Character
instances ont is_the_chosen_one == True
et tous les autres d'avoir is_the_chosen_one == False
. Comment puis-je assurer au mieux cette contrainte d'unicité est-elle respectée?
Top marques des réponses qui prennent en compte l'importance du respect de la contrainte à la base de données, le modèle et l' (admin), les niveaux de forme!
- Bonne question. Je suis aussi curieux de savoir si il est possible de mettre en place une telle contrainte. Je sais que si vous avez simplement une contrainte unique, vous vous retrouverez avec seulement deux lignes dans votre base de données 😉
- Pas nécessairement: si vous utilisez un NullBooleanField, alors vous devriez être en mesure d'avoir: (un Vrai, un Faux, un certain nombre de valeurs Null).
- Selon ma recherche, @semente réponse, tient compte de l'importance du respect de la contrainte à la base de données, le modèle et l' (admin), les niveaux de forme alors qu'il fournit une excellente solution, même pour un
through
table deManyToManyField
qui a besoin d'ununique_together
contrainte.
Vous devez vous connecter pour publier un commentaire.
Chaque fois que j'ai besoin pour accomplir cette tâche, ce que j'ai fait est de remplacer la méthode d'enregistrement pour le modèle et vérifier si un autre modèle a le drapeau (et le désactiver).
save(self)
àsave(self, *args, **kwargs)
mais la modification a été rejetée. On pouvait tout de réviseurs prendre le temps d'expliquer pourquoi - puisque cela semble être cohérente avec Django, la meilleure pratique.get()
ing le Caractère de l'objet, puissave()
ing à nouveau, vous avez juste besoin de filtre et de mise à jour, qui produit seulement une requête SQL et permet de maintenir la DB compatible:if self.is_the_chosen_one:
<newline>Character.objects.filter(is_the_chosen_one=True).update(is_the_chosen_one=False)
<newline>super(Character, self).save(*args, **kwargs)
À la place de l'aide personnalisée modèle de nettoyage/d'économie, j'ai créé un champ personnalisé substitution de la
pre_save
méthode surdjango.db.models.BooleanField
. Au lieu de lever une erreur si un autre champ a étéTrue
, j'ai fait tous les autres champsFalse
si c'étaitTrue
. Aussi, au lieu de lever une erreur si le champ a étéFalse
et aucun autre domaine a étéTrue
, je l'ai enregistré le champ commeTrue
fields.py
models.py
Return True
àsetattr(model_instance, self.attname, True)
true
si vous supprimez le seultrue
ligne.Je voudrais remplacer la méthode d'enregistrement du modèle et si vous avez défini le type boolean à True, assurez-vous que tous les autres sont mis à False.
J'ai essayé de l'édition de la même réponse par Adam, mais elle a été rejetée pour changer trop de l'original de la réplique. Cette façon est la plus succincte et la plus efficace que le contrôle des autres entrées est faite en une seule requête.
save
dans un@transaction.atomic
transaction. Car il peut arriver que vous supprimez tous les drapeaux, mais avant l'enregistrement échoue et vous vous retrouvez avec tous les caractères qui ne sont pas choisis.La solution suivante est un peu moche mais peut fonctionner:
Si vous définissez is_the_chosen_one de Falsification ou de Aucun il sera toujours NULL. Vous pouvez avoir la valeur NULL comme beaucoup comme vous voulez, mais vous ne pouvez en avoir un Vrai.
En essayant de joindre les deux bouts avec les réponses ici, je trouve que certains d'entre eux traitent du même problème avec succès, et chacun est adapté à différentes situations:
Je choisirais:
@semente: Respecte la contrainte à la base de données, le modèle et l'admin niveaux de forme alors qu'il remplace l'ORM de Django le moins possible. En outre, il peut
probablementêtre utilisé à l'intérieur d'unthrough
table d'unManyToManyField
dans ununique_together
situation.(je vais vérifier cela et rapport)@Flyte: Les accès à la base de données qu'une fois de plus et accepte l'entrée en cours comme l'élu. Propre et élégant.
D'autres solutions ne convient pas pour mon cas, mais viable:
@nemocorp supplante l'
clean
méthode pour effectuer une validation. Cependant, il n'a pas de rapport, dont le modèle est "l'un" et ce n'est pas convivial. Malgré cela, c'est une très belle approche, surtout si quelqu'un n'a pas l'intention d'être aussi agressif que @Flyte.@saul.shanabrook et @Thierry J. serait de créer un champ personnalisé qui serait soit le changement de toute autre "is_the_one" entrée de
False
ou d'élever uneValidationError
. Je suis juste réticents à impement de nouvelles fonctionnalités à mon Django installation sauf si elle est absoletuly nécessaire.@daigorocub: Utilise Django signaux. Je trouve ça d'une approche unique et donne une idée de comment utiliser Django Signaux. Cependant, je ne suis pas sûr de savoir si c'est un -strictement parlant, de la "bonne" utilisation de signaux depuis que je ne peut pas considérer cette procédure comme partie d'un "découplé de l'application".
Vous pouvez utiliser le formulaire ci-dessus pour l'admin ainsi, il suffit d'utiliser
Faire ce qui fait que la validation disponibles dans la base admin forme
Et c'est tout.
Puis-je obtenir des points de répondre à ma question?
problème a été de trouver lui-même dans la boucle, fixe par:
J'ai essayé certaines de ces solutions, et a fini avec un autre, juste pour le plaisir de code essoufflement (ne pas avoir à remplacer les formes ou la méthode save).
Pour que cela fonctionne, le champ ne peut pas être unique dans sa définition, mais le signal permet de s'assurer de ce qui se passe.
En utilisant une approche similaire comme Saül, mais le but légèrement différent:
Cette mise en œuvre de soulever un
ValidationError
lorsque vous tentez de sauvegarder un enregistrement avec une valeur de Vrai.Aussi, j'ai ajouté les
unique_for
argument qui peut être réglé à n'importe quel autre champ dans le modèle, afin de vérifier la véritable particularité uniquement pour les enregistrements ayant la même valeur, par exemple: