Django Unique Together (with foreign keys)

From django 2.2+ versions, it is suggested to use constraint & Index as model class meta option:

https://docs.djangoproject.com/en/3.2/ref/models/options/#django.db.models.Options.unique_together

https://docs.djangoproject.com/en/3.2/ref/models/options/#django.db.models.Options.constraints

class UniqueConstraintModel(models.Model):
    race_name = models.CharField(max_length=100)
    position = models.IntegerField()
    global_id = models.IntegerField()
    fancy_conditions = models.IntegerField(null=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                name="unique_constraint_model_global_id_uniq",
                fields=('global_id',),
            ),
            models.UniqueConstraint(
                name="unique_constraint_model_fancy_1_uniq",
                fields=('fancy_conditions',),
                condition=models.Q(global_id__lte=1)
            ),
            models.UniqueConstraint(
                name="unique_constraint_model_fancy_3_uniq",
                fields=('fancy_conditions',),
                condition=models.Q(global_id__gte=3)
            ),
            models.UniqueConstraint(
                name="unique_constraint_model_together_uniq",
                fields=('race_name', 'position'),
                condition=models.Q(race_name='example'),
            )
        ]

You can't.

The unique_together clause is directly translated to the SQL unique index. And you can only set those on columns of a single table, not a combination of several tables.

You can add validation for it yourself though, simply overwrite the validate_unique method and add this validation to it.

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


My 2 cents, complementing the accepted response from @Wolph

You can add validation for it yourself though, simply overwrite the validate_unique method and add this validation to it.

This is a working example code someone could find usefull.

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().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',
            )

My solution was to use Django's get_or_create. By using get_or_create, a useless get will occur if the row already exists in the database, and the row will be created if it does not exist.

Example:

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