【问题标题】:Django REST Framework: Unique_together validation on SerializersDjango REST Framework:序列化器上的 Unique_together 验证
【发布时间】:2014-05-02 11:56:54
【问题描述】:

如果序列化程序实例在模型端的unique_together 约束失败,则serializer.is_valid() 返回True 存在问题。

我有没有办法在序列化程序中指定强制执行unique_together 约束?

【问题讨论】:

  • 你能把有问题的模型包括进来吗?

标签: django django-rest-framework


【解决方案1】:

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_afoo_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."
    ]
}

我希望这对您有所帮助,即使这是一个有点过时的问题;-)

【讨论】:

    【解决方案2】:

    我需要这个来覆盖默认消息。由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

    【讨论】:

    • 我需要检查转换模型字段小写值的验证。是否可以使用默认验证或编写自定义。
    【解决方案3】:

    不幸的是,安德烈亚斯的回答并不完整,因为它在更新的情况下不起作用。

    相反,你会想要更多类似的东西:

    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 会产生什么后果
    • @fangsterr 你是对的,你可以这样做:objects.filter(field1=field1, field2=field2).exists() 但是,如果你正在更新一个对象,一个匹配的对象将会存在——你正在更新的那个,它这就是 id 比较的意义所在。
    • 有点晚了哈哈,但我认为它会在验证之前更新序列化程序,因此不需要 if if self.object and obj.id == self.object.id:,因为不应该被调用,至少这是我在测试中发现的。
    【解决方案4】:

    是的,你可以在序列化器的.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
    

    您在模型中设置的唯一约束是用于创建数据库约束,而不是用于验证。

    【讨论】:

      【解决方案5】:

      有同样的问题,从这个答案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')
      

      【讨论】:

        【解决方案6】:

        好吧,这有点愚蠢,除了我之外的任何人都不太可能犯这个错误,但是我将相同的模型放在两个序列化程序类中,结果我遇到了这个问题

        希望我的错误能帮助别人!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-06
          • 1970-01-01
          • 2023-03-18
          • 2020-05-23
          • 2015-06-29
          • 2016-11-21
          • 2012-08-13
          • 2016-12-10
          相关资源
          最近更新 更多