【问题标题】:losing fields during validation - nested serializer create()在验证期间丢失字段 - 嵌套序列化程序 create()
【发布时间】:2017-12-07 00:36:43
【问题描述】:

我不明白为什么要在validated_data 中删除特定字段。

当 POST 输入到 create() 一个新实例时,以下行: thisLabel = ClassificationLabel.objects.get(identifier=label.identifier) 由于标识符属性不存在而引发错误:

AttributeError at /api/v1/policies/ 

'collections.OrderedDict' object has no attribute 'identifier

我在 Django REST 框架中有以下序列化程序:

serializers.py:

class ClassificationLabelDetailSerializer(serializers.ModelSerializer):

    class Meta:
        model = ClassificationLabel
        fields = ('displayName', 'helpText', 'identifier', 'backgroundColour', 'foregroundColour', 'comment', 'description', 'lastChanged', 'revision')
        read_only_fields = ('identifier', 'lastChanged', 'revision',)


class PolicySerializer(serializers.ModelSerializer):
    labels = ClassificationLabelDetailSerializer(many=True)

    class Meta:
        model = Policy
        fields = ('displayName', 'identifier', 'labels', 'lastChanged', 'description', 'comment')
        read_only_fields = ('identifier', 'lastChanged',)

    def create(self,validated_data):
        labelData = validated_data.pop('labels')
        thisPolicy = Policy.objects.create(**validated_data)
        for label in labelData:
            for k, v in label.items():
                print(k, v)
            thisLabel = ClassificationLabel.objects.get(identifier=label.identifier)#insert organisational filter here
            PolicyMemberClassificationLabel.objects.create(policy=thisPolicy, label=thisLabel, order=index)
        return thisPolicy

models.py:

class ClassificationLabel(models.Model):
    displayName = models.CharField(max_length = 32)
    helpText = models.TextField(max_length = 140, blank=True)
    backgroundColour = models.CharField(max_length=8)
    foregroundColour = models.CharField(max_length=8)
    description = models.TextField(max_length = 256, blank=True)
    comment = models.TextField(max_length = 1024, blank=True)
    lastChanged = models.DateTimeField(auto_now=True, editable=False)
    identifier = models.CharField(max_length = 128, blank=True, editable=False)
    revision = models.PositiveIntegerField(default=1, editable=False)

    def __str__(self):
        return self.displayName + " - " + self.identifier

    def save(self, *args, **kwargs):
        self.revision += 1
        #the following code generates a unique identifier and checks it for collisions against existing identifiers
        if not self.identifier:
            stringCheck = False
            while stringCheck is False:
                newString = str(uuid.uuid4())
                newString.replace('-', '')
                doesStringExist = ClassificationLabel.objects.filter(identifier=newString).exists()
                if doesStringExist is False:
                    stringCheck = True
            self.identifier = newString
        super(ClassificationLabel, self).save(*args, **kwargs) # Call the "real" save() method.


class Policy(models.Model):
    description = models.TextField(max_length = 256, blank=True)
    comment = models.TextField(max_length = 1024, blank=True)
    lastChanged = models.DateTimeField(auto_now =True)
    displayName = models.CharField(max_length = 64)
    identifier = models.CharField(max_length = 128, blank=True)
    labels = models.ManyToManyField(ClassificationLabel, through='PolicyMemberClassificationLabel')
    revision = models.PositiveIntegerField(default=1)

    class Meta:
        verbose_name_plural = 'Policies'

    def __str__(self):
        return self.displayName + " - " + self.identifier

    def save(self, *args, **kwargs):
        self.revision += 1
        #the following code generates a unique identifier and checks it for collisions against existing identifiers
        if not self.identifier:
            stringCheck = False
            while stringCheck is False:
                newString = str(uuid.uuid4())
                newString.replace('-', '')
                doesStringExist = Policy.objects.filter(identifier=newString).count()
                if doesStringExist == 0:
                    stringCheck = True
            self.identifier = newString
            super(Policy, self).save() # Call the "real" save() method.


class PolicyMemberClassificationLabel(models.Model):
    label = models.ForeignKey(ClassificationLabel, related_name='memberLabels')
    policy = models.ForeignKey(Policy, related_name='parentPolicy')
    order = models.PositiveSmallIntegerField(blank=True)

当通过 POST 发送以下内容时,它会从 valid_data 中的嵌套表示中删除标识符、lastChanged 和修订字段。

{
  "labels": [
    {
      "displayName": "Test name",
      "helpText": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book. Wayfarers sartorial authentic, small batch readymade disrupt col",
      "identifier": "fa27e9bd-5007-4874-b10c-46b63c7c8a86",
      "backgroundColour": "#808900",
      "foregroundColour": "#000000",
      "comment": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book.",
      "description": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book.",
      "lastChanged": "2017-07-03T09:26:20.450681Z",
      "revision": 2
    },
    {
      "displayName": "Test name 1",
      "helpText": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book.",
      "identifier": "29c968dd-8b83-4374-962d-32b9ef527e1b",
      "backgroundColour": "#9f0500",
      "foregroundColour": "#FFFFFF",
      "comment": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book.",
      "description": "Wayfarers sartorial authentic, small batch readymade disrupt coloring book.",
      "lastChanged": "2017-07-03T09:25:52.955293Z",
      "revision": 2
    }
  ]
}
  • 我可以看到序列化程序 .is_valid() 对于 PolicySerializer 是 True
  • 当我查看 valid_data 时,这三个字段缺失(其余字段都在那里)
  • 我已尝试将 read_only_fields 注释掉,但这似乎没有任何区别
  • 我一直在参考 DRF 文档here
    • 我确保客户端的内容类型设置正确,因为有类似的问题here
    • 其他人seem也有类似的问题

我的问题:如何在嵌套表示的validated_data 中获取标识符字段?

【问题讨论】:

  • “发送有效负载时”是什么意思?是 POST 请求吗?
  • 如果是 POST 请求,那么您需要了解序列化程序不会接受定义为只读的字段..
  • 感谢@FazilZaid - 这是一个 POST 请求。我会看看省略嵌套序列化程序上的 read_only_fields 是否会改变任何事情
  • 我尝试从嵌套序列化程序 ClassificationLabelDetailSerializer 中删除 read_only_fields 但这并没有任何区别
  • 能否请您提供您的模型? “标识符”字段是什么类型的字段?

标签: django django-rest-framework


【解决方案1】:

在模型上将editable 设置为False 的字段在序列化程序上默认为read_only。 [http://www.django-rest-framework.org/api-guide/serializers/#specifying-read-only-fields]

您应该使用不同的序列化程序进行创建,其中将明确给出字段,如下所示:

class ClassificationLabelDetailSerializer(serializers.ModelSerializer):
    identifier = serializers.CharField()

    class Meta:
        model = ClassificationLabel
        fields = ('displayName', 'helpText', 'backgroundColour', 'foregroundColour', 'comment', 'description', 'lastChanged', 'revision')

【讨论】:

  • 感谢@antash - 就是这样。对此的一些额外说明: 1. 如果删除字段的 editable=False 参数,则需要执行数据库迁移 2. 带有 editable=False 的字段不需要包含在 read_only_fields 中 - 它们是自动包含
【解决方案2】:

ClassificationLabelDetailSerializer 中,您已将identifier 设置为read_only 字段,并且文档指出:

API 输出中包含只读字段,但不应包含 在创建或更新操作期间包含在输入中

这意味着它们不会被传递给validated_data,因为它们不应该用于写操作。

read_only 字段中删除identifier,它应该可以工作。如果您需要在其他地方使用identifier 作为read_only 的序列化程序,那么您应该为嵌套标签创建另一个序列化程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-07
    • 2019-02-19
    • 2018-10-06
    • 1970-01-01
    • 2018-07-06
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多