我有2个具有多对一关系的模型(每个事件可以有1个任务),所以我已经设置了一个与相关名称相反的ForeignKey关系,如下所示。我希望能够在事件的POST上创建一个或多个任务,所以我使用嵌套的序列化器,如documentation for writable nested serializers中显示的Django Rest Framework(我已经使用了这个方法并取得了巨大的成功在过去)。
我可以毫无困难地创建一个任务,我可以创建一个没有问题的事件(使用tasks=[]
)。
问题在于,这一次,无论出于何种原因,如果我嵌套我用于在tasks
数组中创建任务的完全相同的有效负载,我会收到以下错误:ValueError: "<Task: Task object>" needs to have a value for field "id" before this many-to-many relationship can be used.
。< / p>
由于以下几个原因,这令人困惑:
tasks=[]
assignees
上出错,但我只是给它一组主键,而不是对象。而且,只有POST才能创建新任务。views.py
运行命令会显示serializer.is_valid()
返回True
,但在serializer.save()
时,我收到错误(如下所示,完整错误)。活动模式
class Event(models.Model):
owner = models.ForeignKey(ExtendedUser, null=True, related_name='calendar_events')
site = models.ForeignKey(Site, null=True, blank=True, default=None, related_name='calendar_events')
title = models.CharField(max_length=200, blank=True, default='')
description = models.TextField(max_length=200, blank=True, default='')
invitees = models.ManyToManyField(ExtendedUser, default=[], blank=True, related_name='events_invited')
start = models.DateTimeField(null=True, default=None)
stop = models.DateTimeField(null=True, default=None)
frequency = models.ForeignKey(Frequency, null=True, default=None, blank=True)
recurring = models.BooleanField(default=False)
all_day = models.BooleanField(default=False)
# Administrative Fields
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
任务模型
class Task(models.Model):
owner = models.ForeignKey(ExtendedUser, null=True, related_name='tasks')
site = models.ForeignKey(Site, null=True, blank=True, default=None, related_name='tasks')
assignees = models.ManyToManyField(ExtendedUser, default=[], blank=True, related_name='tasks_assigned')
title = models.CharField(max_length=200, blank=True, default='')
description = models.TextField(max_length=200, blank=True, default='')
type = models.ForeignKey(TaskType, null=True, default=None)
template = models.ForeignKey(Template, null=True, default=None)
data = JSONField(null=True, blank=True, default=None)
completed = models.BooleanField(default=False)
completed_at = models.DateTimeField(null=True, blank=True, default=None)
completed_by = models.ForeignKey(ExtendedUser, null=True, blank=True, default=None, related_name='task_completed')
priority = models.ForeignKey(Priority, null=True, default=None, related_name='task')
# Optionally, a task can be connected to an Event or Workflow
event = models.ForeignKey(Event, null=True, default=None, related_name='tasks')
workflow = models.ForeignKey(Workflow, null=True, default=None, related_name='tasks')
# Administrative Fields
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
GET / POST的任务视图
class UserTaskListCreateView(generics.ListCreateAPIView):
permission_classes = [IsAuthenticated, IsCurrentUserOrAgentOrDeveloper]
serializer_class = TaskDetailSerializer
pagination_class = Pagination
def get_queryset(self):
user_slug = self.kwargs['user_slug']
return Task.objects.filter(
Q(owner__slug=user_slug) | Q(assignees__slug=user_slug)
)
def create(self, request, user_slug):
request.data['owner'] = ExtendedUser.objects.get(slug=user_slug).id
serializer = TaskSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
POST的事件视图
class UserScheduleListCreateView(generics.ListCreateAPIView):
permission_classes = [IsAuthenticated, IsCurrentUserOrAgentOrDeveloper]
serializer_class = EventDetailSerializer
pagination_class = Pagination
def create(self, request, user_slug):
request.data['owner'] = ExtendedUser.objects.get(slug=user_slug).id
serializer = EventSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
任务序列化程序
class TaskSerializer(serializers.ModelSerializer):
site = SiteField()
data = serializers.JSONField()
class Meta:
model = Task
fields = [
'id',
'owner',
'site',
'assignees',
'type',
'title',
'description',
'template',
'data',
'completed',
'completed_at',
'completed_by',
'priority',
'event',
'workflow',
'created',
'updated'
]
事件序列化程序
class EventSerializer(serializers.ModelSerializer):
site = SiteField()
tasks = TaskSerializer(many=True)
class Meta:
model = Event
fields = [
'id',
'owner',
'site',
'title',
'description',
'invitees',
'start',
'stop',
'frequency',
'recurring',
'all_day',
'tasks',
'created',
'updated'
]
def create(self, validated_data):
invitees = validated_data.pop('invitees', None)
tasks_data = validated_data.pop('tasks', None)
event = Event.objects.create(**validated_data)
for invitee in invitees:
event.invitees.add(invitee)
event.save()
if tasks_data is not None:
for task_data in tasks_data:
task = Task.objects.create(event=event, **task_data)
return event
ValueError Traceback (most recent call last)
<ipython-input-12-4bde7bc40c4c> in <module>()
----> 1 serializer.save()
~/.virtualenvs/tomis/lib/python3.5/site-packages/rest_framework/serializers.py in save(self, **kwargs)
212 )
213 else:
--> 214 self.instance = self.create(validated_data)
215 assert self.instance is not None, (
216 '`create()` did not return an object instance.'
~/Desktop/sandboxes/python/tomis-backend/component/calendar/serializers.py in create(self, validated_data)
69 print('event:', event)
70 print('task_data:', task_data)
---> 71 Task.objects.create(event=event, **task_data)
72 print('thingy')
73 # event.tasks.create(**task_data)
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/manager.py in manager_method(self, *args, **kwargs)
83 def create_method(name, method):
84 def manager_method(self, *args, **kwargs):
---> 85 return getattr(self.get_queryset(), name)(*args, **kwargs)
86 manager_method.__name__ = method.__name__
87 manager_method.__doc__ = method.__doc__
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/query.py in create(self, **kwargs)
390 and returning the created object.
391 """
--> 392 obj = self.model(**kwargs)
393 self._for_write = True
394 obj.save(force_insert=True, using=self.db)
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/base.py in __init__(self, *args, **kwargs)
566 if prop in property_names or opts.get_field(prop):
567 if kwargs[prop] is not _DEFERRED:
--> 568 _setattr(self, prop, kwargs[prop])
569 del kwargs[prop]
570 except (AttributeError, FieldDoesNotExist):
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py in __set__(self, instance, value)
534 RemovedInDjango20Warning, stacklevel=2,
535 )
--> 536 manager = self.__get__(instance)
537 manager.set(value)
538
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py in __get__(self, instance, cls)
511 return self
512
--> 513 return self.related_manager_cls(instance)
514
515 def _get_set_deprecation_msg_params(self):
~/.virtualenvs/tomis/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py in __init__(self, instance)
828 raise ValueError('"%r" needs to have a value for field "%s" before '
829 'this many-to-many relationship can be used.' %
--> 830 (instance, self.pk_field_names[self.source_field_name]))
831 # Even if this relation is not to pk, we require still pk value.
832 # The wish is that the instance has been already saved to DB,
ValueError: "<Task: Task object>" needs to have a value for field "id" before this many-to-many relationship can be used.
答案 0 :(得分:2)
问题在于任务的assignees
字段。由于它是多对多的,您应该在添加受让人之前单独保存每个任务:
if tasks_data is not None:
for task_data in tasks_data:
assignees = task_data.pop('assignees')
task = Task.objects.create(event=event, **task_data)
task.assignees = assignees
task.save()