Django Unique Together (с внешними ключами)

У меня есть ситуация, когда я хочу использовать Meta-опции unique_together для принудительного применения определенного правила, здесь промежуточная модель:

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 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"))

и здесь UserProfile:

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

Спасибо.

Ответ 1

Вы не можете.

Предложение unique_together непосредственно переводится в уникальный индекс SQL. И вы можете устанавливать их только в столбцах одной таблицы, а не в комбинации нескольких таблиц.

Вы можете добавить для него валидацию, но просто перезапишите метод validate_unique и добавьте к нему эту проверку.

Документы: http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.validate_unique

Ответ 2

Моим решением было использовать Django get_or_create. Используя get_or_create, бесполезный get будет возникать, если строка уже существует в базе данных, а строка будет создана, если она не существует.

Пример:

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

Ответ 3

Мои 2 цента, дополняющие принятый ответ от @Wolph

Вы можете добавить валидацию для него самостоятельно, просто переписав метод validate_unique и добавив в него эту валидацию.

Это рабочий пример кода, который кто-то может найти полезным.

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