Django rest framework M2M序列化程序保存嵌套对象
我有两个模型:任务和场景。任务是预先创建的,而场景则是通过包含少量现有任务来创建的。重要的是,在场景中,任务必须按照特定的顺序安排,而不是根据它们的IDDjango rest framework M2M序列化程序保存嵌套对象,django,django-rest-framework,Django,Django Rest Framework,我有两个模型:任务和场景。任务是预先创建的,而场景则是通过包含少量现有任务来创建的。重要的是,在场景中,任务必须按照特定的顺序安排,而不是根据它们的ID class Task(models.Model): stakeholder = models.ForeignKey(User, related_name='tasks', blank=True, ) project = models.ForeignKey(Project, related_name='project_tasks'
class Task(models.Model):
stakeholder = models.ForeignKey(User, related_name='tasks', blank=True, )
project = models.ForeignKey(Project, related_name='project_tasks' )
title = models.CharField(max_length=50, blank=True, null = True, )
...
class Scenario(models.Model):
stakeholder = models.ForeignKey(User, related_name='scenarios', blank=True,)
tasks = models.ManyToManyField(Task, blank=True)
序列化程序:
class TaskSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
class Meta:
model = Task
fields = '__all__'
class ScenarioSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, required=False)
class Meta:
model = Scenario
fields = '__all__'
def get_or_create_task(self, data):
qs = Task.objects.filter(pk=data.get('id'))
if qs.exists():
return qs.first()
task = Task.objects.create(**data)
return task
def add_tasks(self, instance, tasks):
for task_data in tasks:
task = self.get_or_create_task(task_data)
instance.tasks.add(task)
def create(self, validated_data):
tasks = validated_data.pop('tasks')
instance = Scenario.objects.create(**validated_data)
self.add_tasks(instance, tasks)
return instance
def update(self, instance, validated_data):
tasks = validated_data.pop('tasks', [])
instance = super().update(instance, validated_data)
self.add_tasks(instance, tasks)
return instance
我有几个要求:
在检索场景时,我希望检索相应的任务对象,而不仅仅是它们的id,这就是为什么TaskSerializer中存在行id=serializers.IntegerField()
使用此代码成功创建了一个新任务,但任务更新失败,例如,/api/tasks/1
,因为它还需要请求正文中的id。没有id=serializers.IntegerField()
任务创建和更新都成功
另外,如果没有id=serializers.IntegerField()
,则TaskSerializer场景创建成功,并以正确的顺序返回包含的任务,例如task3、task1、Task5。但是,无论场景创建时包含哪些任务,都会在数据库中再次创建这些任务(当然是使用连续ID)
取消注释id=serializers.IntegerField()
会成功创建场景,并且不会自动创建额外的任务(这很好),但场景会按其id的顺序返回任务,即如果场景是使用Task3、task1、task5创建的,那么当您获得场景时,会返回task1、Task3、task5
为什么会这样
额外说明:我不打算在场景创建时创建任务,任务总是在创建之前创建,场景将在创建之后通过附加一些现有任务来创建您可以尝试将
任务设置为只读
,并从上下文中获取数据,要保存创建的订单,您可以使用extra-select
class ScenarioSerializer(serializers.ModelSerializer):
tasks = serializers.SerializerMethodField()
class Meta:
model = Scenario
fields = '__all__'
def get_tasks(self, obj):
qs = obj.tasks.extra(
select={'creation_seq': 'scenario_scenario_tasks.id'}
).order_by("creation_seq")
return TaskSerializer(qs, many=True).data
def get_or_create_task(self, data):
qs = Task.objects.filter(pk=data.get('id'))
if qs.exists():
return qs.first()
serializer = TaskSerializer(data=data)
serializer.is_valid(raise_exception=True)
task = serializer.save()
return task
def add_tasks(self, instance, data):
tasks = data.get('tasks', [])
for task_data in tasks:
task = self.get_or_create_task(task_data)
instance.tasks.add(task)
def create(self, validated_data):
instance = super().create(validated_data)
# for python 2
# instance = super(ScenarioSerializer, self).create(validated_data)
data = self.context.get('request').data
# HERE ^^^^^^^^^^
self.add_tasks(instance, data)
return instance
def update(self, instance, validated_data):
instance = super().update(instance, validated_data)
# for python 2
# instance = super(ScenarioSerializer, self).update(instance, validated_data)
data = self.context.get('request').data
# HERE ^^^^^^^^^^
self.add_tasks(instance, data)
return instance
在这里,您可以查看drfdoc基于类视图的完整示例任务排序的标准是什么?创建场景时,在请求中发送任务的顺序,应该保留该顺序。e、 g.{tasks:[{id=5,…},{id=2,…},{id=4,…}]}
那么应该保留顺序。当我在TaskSerializer中注释id=serializers.IntegerField()
时,顺序将保留。您应该在代码中控制顺序。您可以为中间表(到
)创建一个模型,添加一个时间戳字段并在类中设置默认顺序。要在更新操作中删除场景中所有存在的任务,您可以使用instance.tasks.all().delete()
谢谢!成功了:)