Django REST Framework中如何批量保存Visit与VisitData父子表
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:
- You’re using an undefined
visitsvariable instead of reading fromrequest.data(the actual data sent by the client). - You’re not passing the top-level
userfield from your request into each individual visit entry (which yourVisVisitsmodel likely requires). - 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_valuemethod bridges the gap between your request’s field names and what your model/serializer expects. - User Field Propagation: We pass the top-level
userfield to each visit entry, ensuring yourVisVisitsmodel gets the required foreign key.
Quick Testing Tips
- Use tools like Postman or curl to send requests with the
Content-Type: application/jsonheader. - Add
print(request.data)in your view to debug the incoming data structure. - Check your model relationships to ensure the
VisVisitDataforeign key toVisVisitsis properly defined.
内容的提问来源于stack exchange,提问作者Raju Singh

