![](/img/trans.png)
[英]unique_together django with foreign keys raise Validation error
[英]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.