在 Django REST Framework 中处理嵌套对象表示

Posted

技术标签:

【中文标题】在 Django REST Framework 中处理嵌套对象表示【英文标题】:Handling nested object representations in Django REST Framework 【发布时间】:2020-07-11 10:44:58 【问题描述】:

我在处理嵌套序列化程序 (DRF) 时遇到了一些问题。

我想用这样的结构收集数据:


    'some_tekst': 'test_text',
    'measurements': [
        'int_test': 1, 'char_test': '1',
        'int_test': 2, 'char_test': '2'
    ]

我正在使用文档 (https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects) 并且正在查看 (https://medium.com/@raaj.akshar/creating-reverse-related-objects-with-django-rest-framework-b1952ddff1c) 并且仍然有 HTTP 400 响应:

'"measurements":["此字段为必填项。"]'

models.py

class Data(models.Model):
    datetime = models.CharField(max_length=100)

    def __str__(self):
        return 'just testing'


class Measurement(models.Model):
    # EDIT:
    # data = models.ForeignKey(Data, on_delete=models.CASCADE) #deleted

    char_test = models.CharField(max_length=100)
    test = models.IntegerField(default=0)


    def __str__(self):
        return self.char_test

serializers.py:

class MeasurementSerializer(serializers.ModelSerializer):
    class Meta:
        model = Measurement
        fields = '__all__'

class DataSerializer(serializers.ModelSerializer):
    # EDIT:
    #measurements = MeasurementSerializer(many=True)
    measurements = MeasurementSerializer(many=True, source='measurement_set')

    class Meta:
        model = Data
        fields = ['datetime', 'measurements']

    # EDIT:
    #def create(self, validated_data):
    #    measurement_validated_data = validated_data.pop('measurements')
    #    data = Data.objects.create(**validated_data)
    #    Measurement.objects.create(data=data, **measurement_validated_data)
    #    return data
    def create(self, validated_data):
        measurement_validated_data = validated_data.pop('measurements')
        data = Data.objects.create(**validated_data)
        for measurement_data in measurement_validated_data:
            Measurement.objects.create(data=data, **measurement_data)
        return data

views.py

class DataViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    serializer_class = DataSerializer
    queryset = Data.objects.all()


class MeasurementViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    serializer_class = MeasurementSerializer
    queryset = Measurement.objects.all()

还有我的简单发帖请求:

data = 
    "datetime": "testdatatime",
    "measurements": [
        'test': 1, 'char_test': '1',
        'test': 2, 'char_test': '2',
    ],

r = requests.post(
    'http://127.0.0.1:8000/api/data/',
    data=data,
)

我是不是忘记了什么?

编辑: 有来自 DRF 的 API 端点:

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept


    "name": "Data List",
    "description": "API endpoint that allows groups to be viewed or edited.",
    "renders": [
        "application/json",
        "text/html"
    ],
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "actions": 
        "POST": 
            "datetime": 
                "type": "string",
                "required": true,
                "read_only": false,
                "label": "Datetime",
                "max_length": 200
            ,
            "measurements": 
                "type": "field",
                "required": true,
                "read_only": false,
                "label": "Measurements",
                "child": 
                    "type": "nested object",
                    "required": true,
                    "read_only": false,
                    "children": 
                        "id": 
                            "type": "integer",
                            "required": false,
                            "read_only": true,
                            "label": "ID"
                        ,
                        "char_test": 
                            "type": "string",
                            "required": true,
                            "read_only": false,
                            "label": "Char test",
                            "max_length": 100
                        ,
                        "test": 
                            "type": "integer",
                            "required": false,
                            "read_only": false,
                            "label": "Test",
                            "min_value": -2147483648,
                            "max_value": 2147483647
                        
                    
                
            
        
    

编辑 2: django 端(控制台)出错

django_web  | Bad Request: /api/data/
django_web  | [31/Mar/2020 22:54:39] "POST /api/data/ HTTP/1.1" 400 44

【问题讨论】:

【参考方案1】:

DRF 无法找出measurement 序列化器字段的来源。执行以下操作之一:

    添加related_name:
class Measurement(models.Model):
    data = models.ForeignKey(Data, on_delete=models.CASCADE, related_name='measurements')
    为序列化器字段指定source 属性
class DataSerializer(serializers.ModelSerializer):
    measurements = MeasurementSerializer(many=True, source='measurement_set')

做一个或另一个,而不是两个。

另外,您的创建函数中存在逻辑错误

    def create(self, validated_data):
        measurement_validated_data = validated_data.pop('measurements') # Remember, this is an array
        data = Data.objects.create(**validated_data)
        for measurement_data in measurement_validated_data:
            Measurement.objects.create(data=data, **measurement_data)
        return data

【讨论】:

您好,感谢您的回复!我编辑了我的帖子 - 你能看一下吗?它仍然不适合我。还是一样的回复 (400) 非常感谢! 您好,您可以粘贴或发送带有您看到的实际回溯/错误的屏幕截图吗? @tomm 您的意思是整个响应吗? (比如 r ? r = requests.post( '127.0.0.1:8000/api/data', data=data, ) 我只有 'Bad Request' - 所以它是 400 代码,正如我之前所说的 "'"measurements":["This field是必需的。"]' "。@GrandPhuba 我在上面添加了 django 响应(控制台)(编辑 2)。但没有任何帮助 如我所见,当我使用 ORM 和 QuerySet 时,一切正常。所以问题是如何使用我上面使用的请求发送这些数据。

以上是关于在 Django REST Framework 中处理嵌套对象表示的主要内容,如果未能解决你的问题,请参考以下文章

django使用rest_framework

Django Rest Framework

Django rest framework 身份和权限验证

Django Rest Framework:非模型服务

django-rest-framework - 在可浏览的 API 中自动生成表单?

在 django-rest-framework 中,是不是可以同时使用 oauth 和 session 身份验证?