Django中request.GET获取参数时出现额外引号导致无法解析的问题
我通过Django的request.GET传递参数时,得到的字典里的statuses值被当成了带引号的字符串,没法直接解析成列表来访问内部数据,这让我很头疼。
具体情况
- 重定向到端点的编码数据:
/turnalerts/api/v2/statuses?statuses=%5B%7B%27conversation%27%3A+%7B%27expiration_timestamp%27%3A+%271735510680%27%2C+%27id%27%3A+%2757f7d7d4d255f4c7987ac3557bf536e3%27%2C+%27origin%27%3A+%7B%27type%27%3A+%27service%27%7D%7D%2C+%27id%27%3A+%27wamid.HBgNMjM0OTAzOTc1NjYyOBUCABEYEjdCMTJFNUZDNzNFQjkxQ0IyRQA%3D%27%2C+%27pricing%27%3A+%7B%27billable%27%3A+True%2C+%27category%27%3A+%27service%27%2C+%27pricing_model%27%3A+%27CBP%27%7D%2C+%27recipient_id%27%3A+%272349039756628%27%2C+%27status%27%3A+%27sent%27%2C+%27timestamp%27%3A+%271735424268%27%7D%5D
- 请求对象:
<rest_framework.request.Request: GET '/turnalerts/api/v2/statuses?statuses=%5B%7B%27conversation%27%3A+%7B%27expiration_timestamp%27%3A+%271735510680%27%2C+%27id%27%3A+%2757f7d7d4d255f4c7987ac3557bf536e3%27%2C+%27origin%27%3A+%7B%27type%27%3A+%27service%27%7D%7D%2C+%27id%27%3A+%27wamid.HBgNMjM0OTAzOTc1NjYyOBUCABEYEjdCMTJFNUZDNzNFQjkxQ0IyRQA%3D%27%2C+%27pricing%27%3A+%7B%27billable%27%3A+True%2C+%27category%27%3A+%27service%27%2C+%27pricing_model%27%3A+%27CBP%27%7D%2C+%27recipient_id%27%3A+%272349039756628%27%2C+%27status%27%3A+%27sent%27%2C+%27timestamp%27%3A+%271735424268%27%7D%5D'>
- 通过
request.GET获取到的数据:
{'statuses': "[{'conversation': {'expiration_timestamp': '1735510680', 'id': '57f7d7d4d255f4c7987ac3557bf536e3', 'origin': {'type': 'service'}}, 'id': 'wamid.HBgNMjM0OTAzOTc1NjYyOBUCABEYEjdCMTJFNUZDNzNFQjkxQ0IyRQA=', 'pricing': {'billable': True, 'category': 'service', 'pricing_model': 'CBP'}, 'recipient_id': '2349039756628', 'status': 'sent', 'timestamp': '1735424268'}]"}
能看到statuses的值被当成了字符串(外面的双引号是Python字符串的标识,不是实际参数内容),没法直接像列表那样访问内部字段。
预期结果
我希望拿到的是这样的结构,statuses是一个列表:
{'statuses': [{'conversation': {'expiration_timestamp': '1735510680', 'id': '57f7d7d4d255f4c7987ac3557bf536e3', 'origin': {'type': 'service'}}, 'id': 'wamid.HBgNMjM0OTAzOTc1NjYyOBUCABEYEjdCMTJFNUZDNzNFQjkxQ0IyRQA=', 'pricing': {'billable': True, 'category': 'service', 'pricing_model': 'CBP'}, 'recipient_id': '2349039756628', 'status': 'sent', 'timestamp': '1735424268'}]}
我的视图代码
class StatusesPayloadLayerView(generics.GenericAPIView): permission_classes = (permissions.AllowAny,) def get(self, request, *args, **kwargs): payload = request.GET data = payload.dict() try: error_code = data['statuses'][0]['errors'][0]['code'] except KeyError: error_code = None
现在因为statuses是字符串,执行这段代码会直接报类型错误,根本没法访问内部的errors字段。我查过URL编码规则,编码后的内容里并没有表示双引号的%22,完全搞不懂为啥会变成字符串。
解决办法
其实这个问题很简单:Django的request.GET本质是QueryDict,它会把所有URL参数都当成字符串存储,不会自动帮你解析成列表、字典这类Python数据结构。你看到的双引号只是Python打印字符串时的格式,不是实际参数里的内容,核心是要把这个字符串形式的列表转成真正的Python列表。
这里给你两种靠谱的处理方式:
方式一:用ast.literal_eval(推荐)
ast.literal_eval是Python标准库的工具,能安全地把字符串形式的Python字面量(比如列表、字典)转成实际的Python对象,而且支持单引号,完美适配你的场景:
import ast class StatusesPayloadLayerView(generics.GenericAPIView): permission_classes = (permissions.AllowAny,) def get(self, request, *args, **kwargs): payload = request.GET data = payload.dict() error_code = None try: # 把字符串解析成Python列表 statuses_data = ast.literal_eval(data['statuses']) # 现在可以正常访问内部数据了 error_code = statuses_data[0]['errors'][0]['code'] except (KeyError, IndexError, SyntaxError): # 处理解析失败、找不到字段的情况 error_code = None return # 这里根据你的需求返回响应
方式二:用json模块
如果想用json模块,因为标准JSON要求键和字符串必须用双引号,所以需要先把字符串里的单引号替换成双引号,再解析:
import json class StatusesPayloadLayerView(generics.GenericAPIView): permission_classes = (permissions.AllowAny,) def get(self, request, *args, **kwargs): payload = request.GET data = payload.dict() error_code = None try: # 替换单引号为双引号,转换成标准JSON格式 json_str = data['statuses'].replace("'", '"') statuses_data = json.loads(json_str) error_code = statuses_data[0]['errors'][0]['code'] except (KeyError, IndexError, json.JSONDecodeError): error_code = None return # 这里根据你的需求返回响应
为啥会出现这种情况?
当你通过URL传递类似列表/字典的复杂结构时,Django不会自动识别并解析,只会把它当作普通的字符串参数存储。你看到的双引号只是Python在展示字符串时的标识,不是实际参数里的内容,所以必须手动解析成Python数据结构才能正常使用。
备注:内容来源于stack exchange,提问作者user24242514

