验证期间丢失字段 - 嵌套序列化器create()

时间:2017-07-03 16:56:39

标签: django django-rest-framework

我不明白为什么在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发送以下内容时,它会从validated_data中的嵌套表示中删除标识符,lastChanged和revision字段。

{
  "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
    }
  ]
}
  • 我可以看到,对于PolicySerializer
  • ,序列化程序.is_valid()为True
  • 当我查看validated_data时,缺少这三个字段(其余的都在那里)
  • 我已尝试评论read_only_fields并且似​​乎没有任何区别
  • 我一直在引用DRF文档here
    • 我确保客户的内容类型设置正确,因为存在类似的问题here
    • 其他人seem遇到类似问题

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

2 个答案:

答案 0 :(得分:1)

ClassificationLabelDetailSerializer中,您已将identifier设置为read_only字段,文档说明:

  

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

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

identifier字段中删除read_only,它应该有效。如果您需要identifier作为read_only的其他位置的序列化程序,则应为嵌套标签创建另一个序列化程序。

答案 1 :(得分:0)

默认情况下,序列化程序上模型上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')