You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework中如何批量保存Visit与VisitData父子表

Fixing DRF Nested Serializer Batch Save Error

Alright, let's tackle this issue where your batch save of VisVisits and VisVisitData is throwing the "writable nested fields" error, even though individual saves work. I’ve gone through your code, and here’s what’s causing the problem and how to fix it:

What’s Going Wrong?

The error message says the .create() method doesn’t support writable nested fields by default, but you did override create() in your VisVisitsSerializer—so why the issue? The main problems are in your view code:

  1. You’re using an undefined visits variable instead of reading from request.data (the actual data sent by the client).
  2. You’re not passing the top-level user field from your request into each individual visit entry (which your VisVisits model likely requires).
  3. There’s a mismatch between the field names in your request data and what your serializers expect, which breaks the validation and creation flow.

Step-by-Step Fix

1. Fix the View to Properly Read Request Data

First, we need to correctly pull the batch data from the request, then loop through each visit entry while passing the required user field. Here’s the corrected view:

from rest_framework.decorators import api_view
from rest_framework import status
from rest_framework.response import Response
from .serializers import VisVisitsSerializer

@api_view(['POST'])
def SaveVisitView(request):
    visits_issues = []
    request_data = request.data

    # Validate the top-level request structure
    if 'visits' not in request_data:
        return Response(
            {"error": "Request must contain a 'visits' array"},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Loop through each visit in the batch
    for visit_item in request_data['visits']:
        # Pass the top-level user to each visit entry
        visit_item['user'] = request_data.get('user')
        serializer = VisVisitsSerializer(data=visit_item)
        
        if serializer.is_valid():
            serializer.save()
        else:
            # Collect validation errors for problematic entries
            visits_issues.append({
                'local_id': visit_item.get('local_id'),
                'validation_issues': serializer.errors
            })

    # Return errors if any, or success message
    if visits_issues:
        return Response(visits_issues, status=status.HTTP_400_BAD_REQUEST)
    return Response(
        {"message": "All visits and their data saved successfully"},
        status=status.HTTP_201_CREATED
    )

2. Align Request Data Fields with Serializers

Your request data uses field names like VisitParamID and VisitParamValue, but your VisVisitDataSerializer expects parameter_id and value_text. We need to map these fields correctly so the serializer can parse the incoming data. Update your VisVisitDataSerializer with a to_internal_value method to handle this mapping:

class VisVisitDataSerializer(serializers.ModelSerializer):
    local_id = serializers.IntegerField(source='app_local_id', read_only=True)
    data_id = serializers.IntegerField(source='vdata_id', read_only=True)
    parameter_id = serializers.IntegerField(source='vparameter_id', required=False)
    value_id = serializers.IntegerField(source='vpvalue_id', required=False)
    value_text = serializers.CharField(source='vpvalue_text', required=False)
    group_no = serializers.CharField(source='group_no', required=False)

    class Meta:
        model = VisVisitData
        fields = ('local_id', 'data_id', 'parameter_id', 'value_id', 'group_no', 'value_text')

    def to_internal_value(self, data):
        # Map request field names to serializer/model field names
        mapped_data = {
            'vparameter_id': data.get('VisitParamID'),
            'vpvalue_text': data.get('VisitParamValue'),
            'group_no': data.get('VisitGroupNo'),
            # Add other mappings as needed based on your model
        }
        # Let the parent class handle the rest of the validation
        return super().to_internal_value(mapped_data)

3. Verify Your Serializer’s create Method

Your existing create method in VisVisitsSerializer looks correct, but double-check that your VisVisitData model has a foreign key to VisVisits (named visit, since you’re passing visit=visit when creating VisVisitData objects). If the foreign key field has a different name, update that line to match.

Why This Works

  • Correct Request Data Handling: We now read directly from request.data, ensuring we’re working with the actual client-sent data.
  • Field Mapping: The to_internal_value method bridges the gap between your request’s field names and what your model/serializer expects.
  • User Field Propagation: We pass the top-level user field to each visit entry, ensuring your VisVisits model gets the required foreign key.

Quick Testing Tips

  • Use tools like Postman or curl to send requests with the Content-Type: application/json header.
  • Add print(request.data) in your view to debug the incoming data structure.
  • Check your model relationships to ensure the VisVisitData foreign key to VisVisits is properly defined.

内容的提问来源于stack exchange,提问作者Raju Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:22:56