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

Django Serializer设置已存在ManyToMany属性时抛Invalid pk不存在错误

错误原因
  • 核心问题出在multipart/form-data类型请求的参数处理逻辑:当请求携带文件时,Django REST Framework会将request.data解析为QueryDict实例,而非JSON请求对应的原生Python字典。QueryDict默认仅支持存储字符串/文件类型的单值,直接对key赋值列表时,会自动将整个列表做字符串序列化,你赋值的['video']最终会被存储为字符串"['video']"。
  • 序列化器校验hash_tags字段时,会将收到的字符串"['video']"当做主键去查询HashTag表,自然找不到对应记录,因此抛出Invalid pk "[\'video\']" - object does not exist.的错误。
  • 不带文件的JSON请求可以正常运行,是因为JSON请求的request.data是原生字典,支持列表类型的原生存储,序列化器可以正常解析列表中的每个主键值。
规范解决方案

方案1(最优):绕过request.data传参,直接在save阶段注入关联标签

你提取hash_tags的逻辑完全在后端实现,不需要通过请求参数传递,因此不需要把hash_tags塞入request.data,可以直接在序列化器保存时传入,彻底规避QueryDict的格式问题:

  1. 首先修改set_request_data_for_post函数,不再操作request.data的hash_tags字段,改为返回提取到的标签对象列表:
from django.http.request import QueryDict

def set_request_data_for_post(request, user_uuid: str):
    if isinstance(request.data, QueryDict):
        request.data._mutable = True
    request.data['creator'] = user_uuid
    post_text = request.data['body']
    hash_tags_list = extract_hashtags(post_text)
    # 直接返回HashTag对象列表,不需要转成主键字符串
    hash_tags = [HashTag.objects.get_or_create(hash_tag=ht)[0] for ht in hash_tags_list]
    if isinstance(request.data, QueryDict):
        request.data._mutable = False
    return request, hash_tags
  1. 调整create_post逻辑,保存序列化器时传入标签列表:
def create_post(request):
    user_uuid = str(request.user.uuid)
    request, hash_tags = set_request_data_for_post(request=request, user_uuid=user_uuid)

    try:
        serializer = CreatePostSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            # 保存时传入hash_tags,DRF会自动处理多对多关联
            post_obj = serializer.save(hash_tags=hash_tags)
    except Exception as e:
        return Response(dict(error=str(e),
                             user_message=error_message_generic),
                        status=status.HTTP_400_BAD_REQUEST)

    return Response(serializer.data, status=status.HTTP_201_CREATED)
  1. 可选:如果不需要前端传入hash_tags,可以直接把hash_tags从CreatePostSerializer的fields列表中移除,避免不必要的校验。

方案2:适配QueryDict的列表赋值规则

如果你一定要通过request.data传递hash_tags,需要用QueryDict专属的setlist方法赋值列表,同时提前修改QueryDict的可编辑属性:

from django.http.request import QueryDict

def set_request_data_for_post(request, user_uuid: str):
    # 先开启QueryDict的可编辑权限
    if isinstance(request.data, QueryDict):
        request.data._mutable = True
    request.data['creator'] = user_uuid
    post_text = request.data['body']
    hash_tags_list = extract_hashtags(post_text)
    hash_tags = [HashTag.objects.get_or_create(hash_tag=ht)[0].hash_tag for ht in hash_tags_list]

    if len(hash_tags) > 0:
        # 用setlist方法赋值列表,不要直接用=赋值
        request.data.setlist('hash_tags', hash_tags)
    # 关闭可编辑权限,避免后续异常
    if isinstance(request.data, QueryDict):
        request.data._mutable = False
    return request

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 00:36:11