Django Rest Framework中遍历带嵌套键的form-data数据方法
处理DRF中Form-Data嵌套数据的访问问题
问题背景
我在使用Django Rest Framework开发项目,需通过form-data提交文档创建企业。当前request.data的格式如下:
{ 'company[company_owner][email]': ['rohansheryabraham@gmail.com'], 'company[company_owner][phone]': ['098765439'], 'company[company_owner][first_name]': ['Mike'], 'company[company_owner][last_name]': ['Tyson'], 'company[company_name]': ['Testing'], 'company[company_email]': ['testing@mail.com'], 'company[company_mobile_phone]': ['980765432'], 'company[company_landline]': ['78765421'], 'company[company_country]': ['India'], 'company[email_otp]': ['783143'], 'company[mobile_otp]': ['256310'], 'company_detail[0][detail]': ['110'], 'company_details[0][documents]': [ '<InMemoryUploadedFile: Screenshot from 2023-11-27 11-50-29.png (image/png)>','<InMemoryUploadedFile: Screenshot from 2023-11-27 11-50-20.png (image/png)>' ] }
与JSON格式不同,无法直接通过company_detail[0]['documents']这种方式访问嵌套数据,当前视图仅能打印原始数据:
@api_view(['POST']) @permission_classes([AllowAny]) @parser_classes([FormParser, MultiPartParser]) def test(request): print(request.data) # logic to create company return Response()
解决方案
方案1:手动解析为嵌套字典
编写一个工具函数,将扁平的form-data键值对转换为嵌套的字典/列表结构,支持处理带括号的键名:
def parse_form_data(data): result = {} for key, value in data.items(): # 处理form-data的列表值:单值取第一个元素,多文件保留列表 if isinstance(value, list): if len(value) == 1 and not isinstance(value[0], type(data['company_details[0][documents]'][0])): value = value[0] # 拆分键的层级,比如"company[company_owner][email]"拆分为["company", "company_owner", "email"] key_parts = key.replace('[', ']').split(']') key_parts = [part for part in key_parts if part] current_level = result for idx, part in enumerate(key_parts): # 处理索引类型的键(如"0") if part.isdigit(): part = int(part) # 初始化列表(如果当前层级不是列表) if not isinstance(current_level, list): current_level = [] # 扩展列表到所需长度,避免索引越界 while len(current_level) <= part: current_level.append({}) current_level = current_level[part] else: # 处理普通键,最后一层赋值,否则初始化空字典 if idx == len(key_parts) - 1: current_level[part] = value else: if part not in current_level: current_level[part] = {} current_level = current_level[part] return result
在视图中使用该函数:
@api_view(['POST']) @permission_classes([AllowAny]) @parser_classes([FormParser, MultiPartParser]) def test(request): parsed_data = parse_form_data(request.data) # 现在可以像访问JSON一样操作嵌套数据 print(parsed_data['company']['company_owner']['email']) print(parsed_data['company_details'][0]['documents']) # 执行创建企业的逻辑 return Response()
方案2:使用DRF Serializer自动解析
这是更符合DRF最佳实践的方式,通过定义序列化器自动解析嵌套结构,同时自带数据验证:
首先定义序列化器类:
from rest_framework import serializers class CompanyOwnerSerializer(serializers.Serializer): email = serializers.EmailField() phone = serializers.CharField() first_name = serializers.CharField() last_name = serializers.CharField() class CompanyDetailSerializer(serializers.Serializer): detail = serializers.CharField() class CompanyDocumentSerializer(serializers.Serializer): documents = serializers.ListField(child=serializers.FileField()) class CompanyCreateSerializer(serializers.Serializer): company_owner = CompanyOwnerSerializer() company_name = serializers.CharField() company_email = serializers.EmailField() company_mobile_phone = serializers.CharField() company_landline = serializers.CharField() company_country = serializers.CharField() email_otp = serializers.CharField() mobile_otp = serializers.CharField() company_detail = CompanyDetailSerializer(many=True) company_details = CompanyDocumentSerializer(many=True)
然后在视图中使用序列化器处理请求数据:
@api_view(['POST']) @permission_classes([AllowAny]) @parser_classes([FormParser, MultiPartParser]) def test(request): serializer = CompanyCreateSerializer(data=request.data) if serializer.is_valid(): # 验证通过后,validated_data即为嵌套好的结构化数据 validated_data = serializer.validated_data print(validated_data['company_owner']['email']) print(validated_data['company_details'][0]['documents']) # 在这里执行企业创建逻辑,比如保存到数据库 return Response({"status": "success"}) return Response(serializer.errors, status=400)
方案对比
- 方案1适合快速需求,无需额外定义序列化器,灵活度高;
- 方案2自带数据校验,代码更规范,适合复杂业务场景和长期维护。
内容的提问来源于stack exchange,提问作者Rohan Abraham
相关产品推荐
相关产品推荐

