簡體   English   中英

Django Unique Together(帶外鍵)

[英]Django Unique Together (with foreign keys)

我有一種情況,我想使用unique_together的 Meta 選項來執行某個規則,這是中間模型:

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)

謝謝。

你不能。

unique_together子句直接轉換為SQL唯一索引。 而且您只能在單個表的列上設置這些,而不是多個表的組合。

不過,您可以自己為其添加驗證,只需覆蓋validate_unique方法並將此驗證添加到其中即可。

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

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

我的解決方案是使用 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)

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

您需要調用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"))
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。

另一種可能的解決方案是從您的模型中將其添加到您的保存方法中:

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM