Unique champs qui autorise les valeurs null dans Django
J'ai le modèle Foo qui a barre champ. La barre de domaine doit être unique, mais autorise les valeurs null dans, ce qui signifie que je veux permettre à plus d'un enregistrement si la barre de champ est null
, mais si elle n'est pas null
les valeurs doivent être uniques.
Voici mon modèle:
class Foo(models.Model):
name = models.CharField(max_length=40)
bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
Et voici le SQL correspondante de la table:
CREATE TABLE appl_foo
(
id serial NOT NULL,
"name" character varying(40) NOT NULL,
bar character varying(40),
CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)
Lors de l'utilisation de l'interface d'administration pour créer plus de 1 foo objets où la barre est null, il me donne une erreur: "Foo avec cette Barre existe déjà."
Cependant lorsque je l'insère dans la base de données (PostgreSQL):
insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)
Cela fonctionne, très bien, cela me permet d'insérer plus de 1 enregistrement avec barre de nul, de sorte que la base de données me permet de faire ce que je veux, c'est juste quelque chose de mal avec l'Django modèle. Des idées?
MODIFIER
La portabilité de la solution autant que DB n'est pas un problème, nous sommes heureux avec Postgres.
J'ai essayé de réglage unique pour un callable, qui a été ma fonction qui renvoie True/False pour des valeurs spécifiques de bar, il ne donnait pas d'erreurs, cependant couture comme il n'a eu aucun effet du tout.
Jusqu'à présent, j'ai supprimé l'unique rédacteur de la bar de la propriété et de la manipulation du bar l'unicité dans l'application, toutefois, toujours à la recherche d'une solution plus élégante. Toutes les recommandations?
- Je ne peux pas commenter mais alors, voici un peu plus de mightyhal: Depuis Django 1.4 vous devez
def get_db_prep_value(self, value, connection, prepared=False)
que l'appel de la méthode. Vérifier groups.google.com/d/msg/django-users/Z_AXgg2GCqs/zKEsfu33OZMJ pour plus d'informations. Méthode suivante fonctionne pour moi, trop: def get_prep_value(self, value): si la valeur=="": #si Django tente de sauver la " chaîne de caractères, envoyer la db Aucun (NULL) retourne Aucun autre: valeur de retour #sinon, il suffit de passer la valeur - J'ai ouvert un Django billet pour cette. Ajouter votre soutien. code.djangoproject.com/ticket/30210#ticket
Vous devez vous connecter pour publier un commentaire.
Django n'a pas considéré comme égal à NULL pour le but de l'unicité contrôles de puisque la le ticket #9039 a été résolu, voir:
http://code.djangoproject.com/ticket/9039
Le problème ici est que l'normalisé valeur "vide" pour une forme CharField est une chaîne vide, non Aucun. Donc, si vous laissez le champ vide, vous obtenez une chaîne vide, ce n'est pas NULL, stockées dans la base de données. Les chaînes vides sont égaux à des chaînes vides pour l'unicité des contrôles, tant en vertu de Django et de la base de règles.
Vous pouvez forcer l'interface d'admin pour stocker la valeur NULL pour une chaîne vide en fournissant votre propre modèle personnalisé forme de Foo avec un clean_bar méthode qui transforme une chaîne de caractères vide en Aucun:
fields
ouexclude
attribut dansModelForm
instances. Vous pouvez contourner ce problème en omettant leMeta
intérieur de la classe à partir de la ModelForm pour une utilisation dans l'admin. Référence: docs.djangoproject.com/en/1.10/ref/contrib/admin/...** modifier 11/30/2015: En python 3, le module mondial
__metaclass__
variable est n'est plus pris en charge.De plus, en tant que de
Django 1.10
laSubfieldBase
classe a été obsolète:Donc, comme l'a suggéré le
from_db_value()
la documentation et ce exemple, cette solution doit être changé:Je pense une meilleure façon de remplacer l'cleaned_data dans l'admin serait à la sous-classe le charfield - de cette façon, n'importe quelle forme, accède au champ, il sera "juste travail". Vous pouvez prendre le
''
juste avant de les envoyer à la base de données, et les captures NULLES juste après il s'agit de la base de données, et le reste de Django ne pas savoir ou de soins. Un rapide exemple:Pour mon projet, j'ai perdu en une
extras.py
fichier qui vit dans la racine de mon site, alors je peux justefrom mysite.extras import CharNullField
dans mon applicationmodels.py
fichier. Le domaine comporte comme un CharField - n'oubliez pas de définirblank=True, null=True
lors de la déclaration du champ, ou autrement Django lèvera une erreur de validation (champ requis) ou de créer une base de colonne qui n'accepte pas la valeur NULL.CharField
être unCharNullField
, vous aurez besoin de le faire en trois étapes. Tout d'abord, ajouteznull=True
sur le terrain, et de migrer que. Ensuite, faire une migration de données à mettre à jour les valeurs vides de sorte qu'ils sont nuls. Enfin, convertir le champ de CharNullField. Si vous convertissez le terrain avant de faire la migration des données, la migration des données de ne pas faire n'importe quoi.from_db_value()
ne devrait pas avoir que des extracontex
paramètre. Il devrait êtredef from_db_value(self, value, expression, connection):
Parce que je suis nouveau sur stackoverflow, je ne suis pas encore permis de répondre à ces questions, mais je tiens à souligner que, d'un point de vue philosophique, je ne peux pas d'accord avec la réponse la plus populaire tot cette question. (par Karen Tracey)
L'OP nécessite son domaine de bar pour être unique si elle a une valeur, et null sinon. Ensuite, il faut que le modèle lui-même assure que c'est le cas. Elle ne peut être laissée à un code externe pour vérifier cela, parce que cela signifie qu'il peut être contourné. (Ou vous pouvez oublier de vérifier si vous écrivez un nouveau point de vue dans le futur)
Par conséquent, pour garder votre code vraiment de la programmation orientée objet, vous devez utiliser une méthode interne de votre Foo modèle. La modification de la méthode save() ou le terrain sont de bonnes options, mais à l'aide d'un formulaire pour ce faire, la plupart n'est certainement pas.
Personnellement, je préfère utiliser le CharNullField suggéré, pour des raisons de portabilité pour les modèles que je pourrais définir à l'avenir.
Le quick fix est à faire :
MyModel.objects.bulk_create()
permettrait de contourner cette méthode.Une autre solution possible
Pour le meilleur ou pour le pire, Django considère
NULL
équivalent àNULL
pour les fins de l'unicité des contrôles. Il n'y a vraiment aucun moyen de contourner cela court de l'écriture de votre propre mise en œuvre du contrôle d'unicité qui considèreNULL
être unique, peu importe combien de fois il se produit dans une table.(et garder à l'esprit que certaines des solutions pd prendre le même point de vue de
NULL
, donc le code est fondé sur une DB idées surNULL
peuvent ne pas être transférables à d'autres)J'ai récemment eu la même exigence. Au lieu de sous-classement de différents domaines, j'ai choisi de remplacer la save() metod sur mon modèle (appelé "Monmodèle" ci-dessous) comme suit:
Si vous avez un modèle Monmodèle et souhaitez my_field être Null ou unique, vous pouvez remplacer le modèle de la méthode d'enregistrement:
De cette façon, le champ ne peut pas être vide ne seront non-vide ou null. les valeurs null ne pas en contradiction avec l'unicité