【发布时间】:2014-05-02 11:56:54
【问题描述】:
如果序列化程序实例在模型端的unique_together 约束失败,则serializer.is_valid() 返回True 存在问题。
我有没有办法在序列化程序中指定强制执行unique_together 约束?
【问题讨论】:
-
你能把有问题的模型包括进来吗?
标签: django django-rest-framework
如果序列化程序实例在模型端的unique_together 约束失败,则serializer.is_valid() 返回True 存在问题。
我有没有办法在序列化程序中指定强制执行unique_together 约束?
【问题讨论】:
标签: django django-rest-framework
ModelSerializer 类具有此功能内置,至少在 djangorestframework>=3.0.0 中,但是如果您使用的 serializer 不包括受您影响的所有字段所有 unique_togetherconstrain,那么当你保存一个违反它的实例时,你会得到一个IntegrityError。例如,使用以下模型:
class Foo(models.Model):
class Meta:
unique_together = ('foo_a', 'foo_b')
a = models.TextField(blank=True)
b = models.TextField(blank=True)
foo_a = models.IntegerField()
foo_b = models.IntegerField(default=2)
以及下面的序列化器和 ViewSet:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = models.Foo
fields = ('a', 'b', 'foo_a')
class FooViewSet(viewsets.ModelViewSet):
queryset = models.Foo.objects.all()
serializer_class = FooSerializer
routes = routers.DefaultRouter()
routes.register(r'foo', FooViewSet)
如果您尝试使用相同的foo_a 和foo_b 集保存两个实例,您将获得IntegrityError。但是,如果我们像这样修改序列化器:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = models.Foo
fields = ('a', 'b', 'foo_a', 'foo_b')
然后您将获得正确的HTTP 400 BAD REQUEST 状态代码,以及响应正文中相应的 JSON 描述性消息:
HTTP 400 BAD REQUEST
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
{
"non_field_errors": [
"The fields foo_a, foo_b must make a unique set."
]
}
我希望这对您有所帮助,即使这是一个有点过时的问题;-)
【讨论】:
我需要这个来覆盖默认消息。由this解决。
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
class SomeSerializer(serializers.ModelSerializer):
"""
Demostrating How to Override DRF UniqueTogetherValidator Message
"""
class Meta:
model = Some
validators = [
serializers.UniqueTogetherValidator(
queryset=model.objects.all(),
fields=('field1', 'field2'),
message=_("Some custom message.")
)
]
Similarly you can specify fields
【讨论】:
不幸的是,安德烈亚斯的回答并不完整,因为它在更新的情况下不起作用。
相反,你会想要更多类似的东西:
def validate(self, attrs):
field1 = attrs.get('field1', self.object.field1)
field2 = attrs.get('field2', self.object.field2)
try:
obj = Model.objects.get(field1=field1, field2=field2)
except StateWithholdingForm.DoesNotExist:
return attrs
if self.object and obj.id == self.object.id:
return attrs
else:
raise serializers.ValidationError('field1 with field2 already exists')
这适用于 PUT、PATCH 和 POST。
【讨论】:
.exists() 而不是直接获取,您也可以提高效率?不确定不验证 ID 会产生什么后果
objects.filter(field1=field1, field2=field2).exists() 但是,如果你正在更新一个对象,一个匹配的对象将会存在——你正在更新的那个,它这就是 id 比较的意义所在。
if self.object and obj.id == self.object.id:,因为不应该被调用,至少这是我在测试中发现的。
是的,你可以在序列化器的.validate()方法中做到这一点。
def validate(self, attrs):
try:
Model.objects.get(field1=attrs['field1'], field2=attrs['field2'])
except Model.DoesNotExist:
pass
else:
raise serializers.ValidationError('field1 with field2 already exists')
return attrs
您在模型中设置的唯一约束是用于创建数据库约束,而不是用于验证。
【讨论】:
有同样的问题,从这个答案https://stackoverflow.com/a/26027788/6473175 我能够让它工作,但必须使用self.instance 而不是self.object。
def validate(self, data):
field1 = data.get('field1',None)
field2 = data.get('field2',None)
try:
obj = self.Meta.model.objects.get(field1=field1, field2=field2)
except self.Meta.model.DoesNotExist:
return data
if self.instance and obj.id == self.instance.id:
return data
else:
raise serializers.ValidationError('custom error message')
【讨论】:
好吧,这有点愚蠢,除了我之外的任何人都不太可能犯这个错误,但是我将相同的模型放在两个序列化程序类中,结果我遇到了这个问题
希望我的错误能帮助别人!
【讨论】: