在Django/REST中如何保存多对多模型?

6 投票
1 回答
4183 浏览
提问于 2025-04-18 12:17

我正在为我的Django应用写一个REST API,但在处理一个模型的POST请求时遇到了问题。

这个模型是关于用户的:

class ProjectNode(models.Model):
    name = models.CharField(max_length=60)
    place = models.CharField(max_length=150)
    time_spent = models.BigIntegerField()
    parent_project = models.ForeignKey(Project, related_name='tasks')
    workers = models.ManyToManyField(User, related_name='tasks_can_do')

    def __str__(self):
        return self.name

User模型目前只包含一个name字段。

这是我为ProjectNode准备的序列化器:

class ProjectNodeSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProjectNode
        fields = ('id', 'name', 'place', 'time_spent', 'workers',)

接下来是API视图(来自views.py):

class WebProjectNodeListView(generics.ListCreateAPIView):
    queryset = ProjectNode.objects.all()
    serializer_class = ProjectNodeSerializer

    def pre_save(self, obj):
        obj.parent_project = Project.objects.get(pk=self.request.DATA['parent_project'])
        for worker_pk in self.request.DATA['workers']:
            obj.workers.add(User.objects.get(pk=worker_pk))
        obj.final_worker = User.objects.get(pk=self.request.DATA['final_workers'])

昨天我尝试了一个更简单的版本,只有Project的外键关系,那时候似乎一切正常,所以我以为使用add也会有效,但在用httpie测试API时出现了错误(我已经添加了一些用户和项目,并且确认我得到了它们的ID)。

这是我的请求:

http POST :8000/api/tasks/ name="newtask" place="home" time_spent:=50 parent_project:=1 workers:=[1]

然后我得到了这个错误:

"<ProjectNode: newtask>" needs to have a value for field "projectnode" before this many-to-many relationship can be used.

错误追踪信息也指向了这行代码:

obj.workers.add(User.objects.get(id=worker_pk))

现在我觉得这个问题可能是因为我在数据库中创建ProjectNode对象之前,试图更新User对象的关系,但我不太确定该如何解决这个问题。

1 个回答

3

DRF(Django REST Framework)在处理嵌套序列化对象或多对多字段时,可能会出现问题。因此,我们需要重写序列化器的创建方法,并在创建项目节点之前先创建或获取多对多模型。你可以尝试在你的序列化器中重写 create(self, validated_data) 方法,并在这个方法里处理你的数据。

举个例子:

我的模型 Project 和 ProjectImages 之间有多对多的关系。在 ProjectSerializer 中,我像这样重写了创建方法。

 def create(self, validated_data):
        try:
            # Remove nested and M2m relationships from validated_data
            images = validated_data.pop('projectimage_set') if 'projectimage_set' in validated_data else []

            # Create project model
            instance = Project(**validated_data)
            if status:
                instance.set_status(status)

            project = instance.save()

            # Create relations
            for image in images:
                ProjectImage.objects.create(project=project, **image)

        except exceptions.ValidationError as e:
            errors_messages = e.error_dict if hasattr(e, 'error_dict') else e.error_list
            raise serializers.ValidationError(errors_messages)

        return project

希望这能帮到你!

撰写回答