我有两个模型:任务和场景。事先创建任务,然后通过包含一些现有任务来创建方案。重要的是在一个场景中,任务必须按照特定的顺序排列,而不是根据他们的ID。
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,这就是为什么行id = serializers.IntegerField()
存在于TaskSerializer中。
使用此代码成功创建了一个新任务,但是任务更新在例如/api/tasks/1
时失败,因为它还期望请求正文中的id。如果没有id = serializers.IntegerField()
,任务创建和更新都会成功。
此外,在TaskSerializer中没有id = serializers.IntegerField()
方案创建成功并且包含的任务以正确的顺序返回,比如task3,task1,Task5。但是,在创建场景时包含的任何任务都会在数据库中再次创建(当然还有连续的ID)。
取消注释id = serializers.IntegerField()
会导致成功创建场景,并且不会自动创建额外任务(这很好),但场景会返回其ID中的任务,即如果使用Task3,task1,task5创建场景然后当你获得一个场景时,你会得到task1,task3,task5。
为什么会这样?
额外注意:我不打算在创建场景时创建任务,事先总是创建任务,然后通过将一些现有任务附加到场景来创建场景
答案 0 :(得分:1)
您可以尝试将tasks
设置为read_only
并从context
获取数据,并保存您可能使用的创建订单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-demo您可以查看drfdoc
基于类的视图的完整示例