8
votes

Django unique_together ne fonctionne pas avec fourrure fiable = aucun

J'ai vu que certains ppl ont eu ce problème devant moi, mais sur les versions plus anciennes de Django, et je cours sur 1.2.1.

J'ai un modèle qui ressemble à: xxx < / pré>

Chaque fois que j'essaie de sauvegarder dans l'administrateur une catégorie avec un parent défini sur Aucun, il fonctionne toujours lorsqu'il y a une autre catégorie avec le même nom et le même nom de parent sur aucun.

idées sur la façon dont Pour résoudre ce problème gracieusement?


0 commentaires

3 Réponses :


11
votes

La contrainte unique ensemble est appliquée au niveau de la base de données et apparaît que votre moteur de base de données n'applique pas la contrainte des valeurs null.

in django 1.2, vous pouvez définir un Procurez la méthode pour que votre modèle fournisse une validation personnalisée. Dans votre cas, vous avez besoin de quelque chose qui vérifie les autres catégories avec le même nom chaque fois que le parent n'est pas. xxx

Si vous modifiez des catégories via l'administrateur Django, la méthode nette sera appelé automatiquement. Dans vos propres vues, vous devez appeler catégorie.fullean () .


4 commentaires

L'approche générale semble bonne ici, mais je ne suis pas la suivante de la logique de si auto-self.parent et de catégorie.Object.filter (nom = self.name) .exists (): qui me regarde comme Il vérifie que le parent existe et une autre catégorie avec le même nom existe. Comment est-ce ce que nous voulons? Cela ne devrait-il pas être plutôt quelque chose comme (non testé) si auto-self.parent == Aucun et folderupload.Object.filter (nom = self.name, parent = aucun) .exists (): ?


Je pense que tu as raison. J'utiliserais parent_id__is null = true au lieu de parent = aucun. Il a probablement besoin d'une exclure () pour ignorer également l'objet actuel.


Je serai absent pendant une semaine, alors ne pourrai pas corriger la réponse. N'hésitez pas à le modifier si vous voulez / peut.


L'esprit que cela a une condition de course ... Un autre objet qui échouerait la validation ci-dessus pourrait être créé entre le moment où la validation actuelle est effectuée et le moment où l'objet actuel est enregistré.



5
votes

J'ai aussi eu ce problème et je l'ai résolu en créant un Supermodel avec la méthode Clean code> (comme Alasdair suggéré) et l'utiliser comme classe de base pour tous mes modèles:

class Base_model(models.Model):
  class Meta:
    abstract=True

  def clean(self):
    """
    Check for instances with null values in unique_together fields.
    """
    from django.core.exceptions import ValidationError

    super(Base_model, self).clean()

    for field_tuple in self._meta.unique_together[:]:
        unique_filter = {}
        unique_fields = []
        null_found = False
        for field_name in field_tuple:
            field_value = getattr(self, field_name)
            if getattr(self, field_name) is None:
                unique_filter['%s__isnull'%field_name] = True
                null_found = True
            else:
                unique_filter['%s'%field_name] = field_value
                unique_fields.append(field_name)
        if null_found:
            unique_queryset = self.__class__.objects.filter(**unique_filter)
            if self.pk:
                unique_queryset = unique_queryset.exclude(pk=self.pk)
            if unique_queryset.exists():
                msg = self.unique_error_message(self.__class__, tuple(unique_fields))
                raise ValidationError(msg)


0 commentaires

1
votes

Malheureusement, pour ceux d'entre nous utilisant PostgreSQL comme moteur de base de données Backend, il n'y aura jamais de solution pour ce problème:

"Actuellement, seuls les index b-arbres peuvent être déclarés uniques.

Lorsqu'un index est déclaré unique, plusieurs lignes de table avec des valeurs indexées égales ne sont pas autorisées. Les valeurs nulles ne sont pas considérées comme égales. Un index unique multicolumnique ne rejette que les cas où toutes les colonnes indexées sont égales dans plusieurs rangées.

postgreSQL crée automatiquement un index unique lorsqu'une contrainte unique ou une clé primaire est définie pour une table. L'index couvre les colonnes qui composent la clé primaire ou une contrainte unique (un indice multicolonné, le cas échéant), et est le mécanisme qui applique la contrainte. "

Source: https: // www. postgresql.org/docs/9.0/indexes-unique.html


0 commentaires