73

我有一种情况,我想使用 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'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"))

这是用户配置文件:

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

谢谢。

4

7 回答 7

87

你不能。

unique_together子句直接翻译为SQL唯一索引。而且您只能在单个表的列上设置这些,而不是多个表的组合。

不过,您可以自己为其添加验证,只需覆盖该validate_unique方法并将此验证添加到其中即可。

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

于 2010-12-14T14:28:59.297 回答
14

我的 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().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',
            )
于 2019-03-18T12:33:53.267 回答
12

我的解决方案是使用 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)
于 2012-07-31T20:28:45.513 回答
5

从 django 2.2+ 版本开始,建议使用约束和索引作为模型类元选项:

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'),
            )
        ]
于 2019-05-22T06:46:56.007 回答
2

您需要调用Models.full_clean()方法来调用 foreignKey 的 validate_unique。您可以覆盖 save() 来调用它

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


    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)

    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"))
于 2019-09-11T09:22:01.677 回答
2
from django.core.exceptions import ValidationError

.....

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

    def validate_unique(self, *args, **kwargs):
        super(UserProfileExtension, self).validate_unique(*args, **kwargs)
        query = UserProfileExtension.objects.filter(extension=self.extension)
        if query.filter(userprofile__client=self.userprofile.client).exists():
            raise ValidationError({'extension':['Extension already exits for userprofile__client',]})

第一个查询是过滤 UserProfileExtension 模型中的所有记录,这些记录与我们放入当前记录的扩展名相同。

然后我们过滤返回的查询以查找它是否已经包含我们在当前记录中传递的 userprofile__client。

于 2019-11-15T18:48:21.023 回答
1

另一种可能的解决方案是从您的模型中将其添加到您的保存方法中:

def save(self, *args, **kwargs):
    unique = self.__class__.objects.filter( extension =self.extension, userprofile=self.userprofile )
    if unique.exists():
        self.id = unique[0].id
    super(self.__class__, self).save(*args, **kwargs)
于 2021-12-23T18:41:32.307 回答