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

Django REST Framework创建文档自动关联当前用户及报错排查

问题根因

你报400和传不传user字段没有关系,核心问题有三个:

  1. 你已经在序列化器里把user设为read_only=True+CurrentUserDefault(),后端会自动从登录态里取当前请求的用户赋值,前端根本不需要传这个字段,传了也会被序列化器直接忽略。
  2. 序列化器校验不通过:你声明了mysource = MySourceSerializer(many=True)但没给它加非必填配置,前端没传这个字段时会直接触发校验错误;同时你前端传的字段叫templateId,但模型里对应字段名是template,字段名不匹配也会校验失败。
  3. 序列化器create方法逻辑有漏洞:你只pop了question_blocks、outline_blocks两个嵌套关联字段,漏了mysource,就算校验通过,创建Document对象时也会因为传入了模型不存在的字段报错。

具体修改步骤

1. 后端修改(Django REST Framework)

首先改serializers.py,给嵌套关联字段加非必填配置,补全create方法的字段处理:

class DocumentSerializer(serializers.ModelSerializer):
    id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
    # 所有嵌套关联字段都加required=False、默认空列表,避免不传就报400
    question_blocks = QuestionBlockSerializer(many=True, required=False, default=list)
    outline_blocks = OutlineBlockSerializer(many=True, required=False, default=list)
    mysource = MySourceSerializer(many=True, required=False, default=list)
    # user字段配置正确,无需修改,会自动取当前登录用户
    user = serializers.PrimaryKeyRelatedField(
        read_only=True, default=serializers.CurrentUserDefault()
    )

    class Meta:
        model = Document
        fields = "__all__"

    def create(self, validated_data):
        # 所有嵌套关联字段都要先pop出来,否则传给Document.create会报未知字段错误
        question_blocks = validated_data.pop("question_blocks")
        outline_blocks = validated_data.pop("outline_blocks")
        mysource = validated_data.pop("mysource") # 补上之前漏的这行
        
        document = Document.objects.create(**validated_data)
        # 关联数据创建逻辑和之前一致,如果不需要mysource字段,直接删掉序列化器里的mysource声明即可
        for qBlock in question_blocks:
            QuestionBlock.objects.create(document=document, **qBlock)
        for oBlock in outline_blocks:
            OutlineBlock.objects.create(document=document, **oBlock)
        document.save()
        return document

然后修改文档对应的视图(ViewSet/APIView),实现「用户只能查询自己的文档」需求,同时保证创建时自动绑定用户:

  • 给视图加权限类permission_classes = [IsAuthenticated],确保只有登录用户能访问接口,否则CurrentUserDefault拿不到有效用户;
  • 重写get_queryset方法,过滤出当前用户所属的文档:
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

class DocumentViewSet(viewsets.ModelViewSet):
    serializer_class = DocumentSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        # 只返回当前登录用户自己创建的文档
        return Document.objects.filter(user=self.request.user)

注意:如果用SessionAuthentication做登录态校验,要确保Django的CORS配置开了CORS_ALLOW_CREDENTIALS = True,同时允许的源不能设为*,必须明确写前端的域名,否则withCredentials的请求会被跨域拦截。

2. 前端修改(React)

直接删掉data里的user字段即可,完全不需要传值,同时把字段名templateId改成template和后端字段对齐:

export const createDocTemp = ({
  router,
  title,
  templateId,
  question_blocks,
  outline_blocks,
}: NewDocument) => {
  const data = {
    // 删掉user相关代码,不需要前端传
    title,
    template: templateId, // 字段名改为template,和后端模型对应
    question_blocks: question_blocks || [], // 空值时传空数组,避免校验报错
    outline_blocks: outline_blocks || [],
  };
  axios({
    method: 'post',
    url: `${process.env.NEXT_PUBLIC_API_URL}/api/v1/documents/`,
    headers: { 'Content-Type': 'application/json' },
    data,
    withCredentials: true,
  })
    .then(function (response) {
      if (response.status === 201) {
        router.push(`/draft/${response.data.id}/`);
      }
    })
    .catch(function (error) {
      // 直接打印error.response.data就能看到DRF返回的具体字段校验错误,不需要特意去Postman复现withCredentials场景
      console.log(error.response.data);
      console.log(error.toJSON());
    });
};

效果验证

改完后两个需求会自动生效:

  • 前端发POST创建文档时不需要传任何用户标识,后端会自动把新建文档的所有者绑定为当前登录用户;
  • 前端发GET请求拉取文档列表时,后端只会返回当前用户自己创建的文档,无法看到其他人的内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:09:23