it-swarm-eu.dev

Django Unique Together (avec clés étrangères)

J'ai une situation où je veux utiliser les options Meta de unique_together pour appliquer une certaine règle, voici le modèle intermédiaire:

class UserProfileExtension(models.Model):
    extension = models.ForeignKey(Extension, unique=False)
    userprofile = models.ForeignKey(UserProfile, unique=False)
    user = models.ForeignKey(User, unique=False)  

    class Meta:
        unique_together = (("userprofile", "extension"),
                           ("user", "extension"),
                           # How can I enforce UserProfile's Client 
                           # and Extension to be unique? This obviously
                           # doesn't work, but is this idea possible without
                           # creating another FK in my intermediary model 
                           ("userprofile__client", "extension"))

et voici UserProfile:

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    client = models.ForeignKey(Client)

Merci.

57
chiurox

Tu ne peux pas.

La clause unique_together Est directement traduite dans l'index unique SQL. Et vous ne pouvez les définir que sur les colonnes d'une seule table, pas sur une combinaison de plusieurs tables.

Vous pouvez cependant ajouter la validation vous-même, simplement remplacer la méthode validate_unique Et y ajouter cette validation.

Documents: http://docs.djangoproject.com/en/dev/ref/models/instances/#Django.db.models.Model.validate_unique

66
Wolph

Ma solution a été d'utiliser get_or_create de Django. En utilisant get_or_create, un get inutile se produira si la ligne existe déjà dans la base de données et la ligne sera créée si elle n'existe pas.

Exemple:

 
extension = Extension.objects.get(pk=someExtensionPK)
userProfile = UserProfile.objects.get(pk=someUserProfilePK)
UserProfileExtension.objects.get_or_create(extension=extension, userprofile=userProfile)
10
Jason Pudzianowski

Mes 2 cents, complément la réponse acceptée de @Wolph

Vous pouvez cependant ajouter vous-même la validation, simplement remplacer la méthode validate_unique et y ajouter cette validation.

Il s'agit d'un exemple de code de travail que quelqu'un pourrait trouver utile.

from Django.core.exceptions import ValidationError


class MyModel(models.Model):

    fk = models.ForeignKey(AnotherModel, on_delete=models.CASCADE)

    my_field = models.CharField(...)  # whatever

    def validate_unique(self, *args, **kwargs):
        super(MyModel, self).validate_unique(*args, **kwargs)

        if self.__class__.objects.\
                filter(fk=self.fk, my_field=self.my_field).\
                exists():
            raise ValidationError(
                message='MyModel with this (fk, my_field) already exists.',
                code='unique_together',
            )
4
Manu

À partir de Django 2.2+ versions, il est suggéré d'utiliser la contrainte et l'index comme méta-option de classe de modèle:

https://docs.djangoproject.com/en/2.2/ref/models/options/#Django.db.models.Options.unique_together

https://docs.djangoproject.com/en/2.2/ref/models/options/#Django.db.models.Options.constraints

1
auvipy