【问题标题】:Django POST validated fields disappearing (NOT NULL constraint failed)Django POST 验证字段消失(NOT NULL 约束失败)
【发布时间】:2020-07-27 14:06:48
【问题描述】:

我有一个带有一些模型的 Django 项目。给我带来麻烦的是这个:

class Job(models.Model):
    patient = models.CharField(max_length=64, null=False, blank=False)
    how = MultiSelectField(choices=HOW_CHOICES, default=1)
    with_who = MultiSelectField(choices=WITH_WHO_CHOICES, default=1)
    accessories = MultiSelectField(choices=ACCESSORIES, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True, null=False, blank=False)
    scheduled = models.DateTimeField(null=True, blank=True)
    arrived = models.DateTimeField(null=True, blank=True)
    started = models.DateTimeField(null=True, blank=True)
    completed = models.DateTimeField(null=True, blank=True)
    scheduled_end = models.DateTimeField(null=True, blank=True)
    detected_end = models.DateTimeField(null=True, blank=True)
    approved = models.BooleanField(default=False)
    area_start = models.ForeignKey(Area, on_delete=models.CASCADE, null=False, related_name='jobs_start')
    area_end = models.ForeignKey(Area, on_delete=models.CASCADE, null=False, related_name='jobs_end')
    comment = models.CharField(max_length=256, null=True, blank=True)
    worker = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='jobs_worker')

我有这个 URL 用于 GET、POST、PUT 和 DELETE。

router.register(r'jobs', views.JobList)

链接到的视图继承自 ModelViewSet:

class JobList(LoginRequiredMixin, viewsets.ModelViewSet):
    queryset = Job.objects.all()
    serializer_class = JobsSerializer

据我了解,只需从 ModelViewSet 继承,API 默认处理 GET、POST 和 PUT。这可能就是为什么在 Web API 中我看到带有 POST 数据的表单(see this screenshot)。视图完美地处理了 GET,但问题出在 POST 请求上。

正如您在screenshot 中看到的,POST 数据只需填写 3 个字段。但在模型中,我声明了更多字段null=False,这些字段与 POST 数据框中的字段不同。所以,一个问题是:

  • API 从哪里获取需要显示的字段信息?

如果我尝试 POST,我会收到以下错误: NOT NULL constraint failed: job_manager_job.area_end_id

我向视图和序列化程序添加了 create() 函数,以便调试 API 进程。此时,我手动添加了要创建的作业所需的一些字段 (see this other screenshot)。如您所见,我在两个相似的键中添加了相同的值,这让我想到了另一个问题:

  • JSON 需要哪些键?模型的字段名称或序列化程序中的字段?

视图如下所示:

class JobList(LoginRequiredMixin, viewsets.ModelViewSet):

    queryset = Job.objects.all()
    serializer_class = JobsSerializer

    def create(self, request, *args, **kwargs):
        json_data = request.data
        job_serializer = self.serializer_class(data=json_data)
        if job_serializer.is_valid():
            job_serializer.save()
            print("valid data")
        pass

还有这样的序列化器:

class JobsSerializer(serializers.ModelSerializer):

    status = serializers.SerializerMethodField('get_status')
    area_from = serializers.StringRelatedField(source='area_start.name', default=None)
    area_to = serializers.StringRelatedField(source='area_end.name', default=None)
    assigned = serializers.SerializerMethodField('get_assigned')
    info = serializers.SerializerMethodField('get_info')

    def create(self, validated_data):
        print(validated_data)
        Job.objects.create(**validated_data)
        pass


    def get_status(self, a_job):
        if a_job.approved:
            return 'Approved'
        if a_job.completed:
            return 'Completed'
        if a_job.started:
            return 'Started'
        if a_job.arrived:
            return 'Arrived'
        if a_job.scheduled:
            return 'Scheduled'
        if a_job.created:
            return 'Created'
        return 'Defined'

    def get_info(self, a_job):
        return get_job_info_options(a_job)

    def get_assigned(self, a_job):
        try:
            a = a_job.worker.first_name + ' ' + a_job.worker.last_name
        except AttributeError:
            a = None
        return a

    class Meta:
        model = Job
        fields = ['pk', 'status', 'scheduled', 'scheduled_end', 'assigned', 'area_from', 'area_to', 'patient', 'info']

问题在于,在调试器中,我看到视图接收到所有 JSON 数据(包括额外的键),这些数据在 self.serializer_class(data=json_data) 中传递给序列化程序,但序列化程序中的 validated_data 包括只有 POST 框中显示的 3 个键。我不知道这些数据是如何在序列化程序中消失的。 检查this screenshot 可以看到序列化器的data={...} 和create 函数中的validated_data 不同

我将不胜感激,不仅能解决这个问题,而且如果有人能回答上述关于 django 如何在内部工作的问题。

提前谢谢你。

【问题讨论】:

  • 一个肯定的原因是 SerializerMethodField 是只读的,因此永远不会出现在验证数据中,但这并不能涵盖所有内容。你能用有效数据显示repr(serializer) 吗?似乎它决定某些字段是只读的,或者只是简单地删除它们。
  • @Melvyn 感谢您的快速关注。我使用 SerializerMethodField 是因为 Job 的“状态”不是模型的属性,并且当视图收到 GET 请求时它是“计算的”。序列化程序的 repr 在最后一个屏幕截图的文本框中。但是在序列化程序中使用 SerializerMethodField 的字段在 POST 中是不需要的。需要的是 area_start 和 area_end(它们是外键)。

标签: python django rest post notnull


【解决方案1】:

我们来看看:

fields = [
    'pk',  # unused in create
    'status',  # on serializer, read only method field
    'scheduled',  # OK
    'scheduled_end',  # OK
    'assigned',  # on serializer, read only method field
    'area_from',  # on serializer, read only string related field
    'area_to',  # on serializer, read only string related field
    'patient',  # OK
    'info'  # on serializer, read only method field
]

我得到了 3 个 OK,所以我认为 DRF 是正确的 :)

问题:

  • API 从哪里获取需要显示的字段信息?

serializer.Meta.fields 中的所有可写字段都以表单结尾。不必为了呈现它们而要求它们。

  • JSON 需要哪些键?模型的字段名称还是序列化程序中的字段?

序列化程序的字段名称。但更准确地说,Parsers are first in the pipeline,它可以让你send camelCase json and get snake_case in validated data。

解决您的问题的最简单方法是PrimaryKeyRelatedField。这意味着消费者需要记住主键,但SlugRelatedField 可以保持可读性。

【讨论】:

  • 哇!我不知道 StringRelatedField 是只读的。所以问题来自序列化程序的定义......那么我该怎么做才能使 area_start 和 area_end 不是只读的(因为创建作业需要它们)。非常感谢!鉴于您在 django 方面的知识,您介意回答我发布的要点问题吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 2019-06-01
  • 2020-03-28
  • 2020-12-19
  • 2016-04-02
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多