在Django/REST中如何保存多对多模型?
我正在为我的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
希望这能帮到你!