【问题标题】:How to serialize data not coming from the request and properly validate it (ModelSerializer in Django Rest Framework)?如何序列化不是来自请求的数据并正确验证它(Django Rest Framework 中的 ModelSerializer)?
【发布时间】:2025-11-24 10:45:01
【问题描述】:

使用 Django Rest Framework 3、基于函数的视图和 ModelSerializer(更具体地说是 HyperlinkedModelSerializer)。

当用户从客户端提交表单时,我有一个视图,它获取请求数据,使用它调用外部 API,然后使用来自外部 API 的数据为模型序列化程序填充数据。 我相信这部分工作正常,根据我的阅读,您应该使用 contextvalidate()

在我的模型序列化程序中,到目前为止我只有一个被覆盖的函数:

from django.core.validators import URLValidator

def validate(self, data):
    if 'foo_url' in self.context:
        data['foo_url'] = self.context['foo_url']
        URLValidator(data['foo_url'])
    if 'bar_url' in self.context:
        data['bar_url'] = self.context['bar_url']
        URLValidator(data['bar_url'])
    return super(SomeSerializer, self).validate(data)

以防万一,相关视图代码如下:

context = {'request': request}
...
context['foo_url'] = foo_url
context['bar_url'] = bar_url
s = SomeSerializer(data=request.data, context=context)
if s.is_valid():
    s.save(user=request.user)
    return Response(s.data, status=status.HTTP_201_CREATED)

现在假设我有正确的想法(我的模型确实从相应的上下文数据中填充了它的 foo_urlbar_url 字段),我感到困惑的是验证是如何不起作用的。如果我给它坏数据,模型序列化器不会拒绝它。

我假设在validate() 中,通过将上下文数据添加到数据中,将在调用is_valid() 时检查数据的有效性。可能不是这样,尤其是当我打印出s 时(在使用序列化程序之后但在调用is_valid() 之前),没有迹象表明请求对象的data 已经填充了来自validate() 的上下文数据(我没有'不知道是否应该)。

所以我尝试直接在 validate() 方法中调用 URLValidators,但似乎仍然无法正常工作。尽管给了它像'asdf'或空的python dict({})这样的无效数据,但没有错误。我的测试断言表明该字段确实包含无效数据,例如“{}”。

这样做的正确方法是什么?

【问题讨论】:

  • 我也不明白它是如何通过 Django 的模型验证的,因为 'asdf' 或 '{}' 不是有效的 URLField 数据。

标签: django validation serialization django-rest-framework


【解决方案1】:

你没有调用验证器。

通过URLValidator(data['bar_url']),您实际上是在使用自定义方案(see the docs) 构建一个url 验证器,仅此而已。正确的代码应该是:

URLValidator()(data['bar_url'])

在哪里构建默认的 url 验证器,然后验证值。


但无论如何我不会使用这种方法,而是直接添加额外的数据(不使用上下文)并让 DRF 通过声明正确的字段来进行验证:

# Somewhere in your view
request.data['bar_url'] = 'some_url'

# In serializer:
class MySerializer(serializers.ModelSerializer):

    bar_url = serializers.URLField()

    class Meta:
        fields = ('bar_url', ...)

回答你的评论

我也不明白这也是怎么做到的 Django的模型验证

看到这个答案: Why doesn't django's model.save() call full_clean()?

默认情况下,Django 不会自动调用.full_clean 方法,因此您可以使用无效值保存模型实例(除非约束在数据库级别)。

【讨论】:

  • 非常感谢,好像我误读了文档。我认为 request.data 在您调用 is_valid() 之前是不可访问的(有点像 Django 表单数据)。我假设 Django 在验证之前使其表单数据不可变是有原因的。像这样编辑请求数据一般可以吗?
  • 好吧,here 解释说,请求是不可变的没有充分的理由。个人觉得这样在请求中添加数据是可以的,这样序列化过程就可以保持不变。
最近更新 更多