代码之家  ›  专栏  ›  技术社区  ›  Blairg23 jfs

valueerror:“<task:task object>”需要有字段“id”的值,然后才能使用此多对多关系

  •  0
  • Blairg23 jfs  · 技术社区  · 8 年前

    问题描述

    我有两个模型有多对一关系(每个事件可以有1个多任务),所以我建立了一个具有相关名称的反向foreignkey关系,如下所示。我希望能够在事件发生后创建这些任务中的一个或多个,因此我使用嵌套序列化程序,如中所示 documentation for writable nested serializers 对于django-rest框架(我在过去使用过这种方法并取得了巨大的成功)。

    我可以毫无困难地创建一个任务,也可以创建一个没有问题的事件(使用 tasks=[] )

    问题是,无论出于什么原因,这次如果我将要用于创建任务的完全相同的有效负载嵌套到 tasks 数组,我得到以下错误: ValueError: "<Task: Task object>" needs to have a value for field "id" before this many-to-many relationship can be used. .

    这是令人困惑的,原因有几个:

    1. taskSerializer自己接受负载以创建新任务。
    2. 事件实际上是被创建的,但是 任务=[]
    3. 事件和任务不是一种多对多的关系,因此它一定是在出错 assignees ,但我只是给它一个主键数组,而不是对象。同样,当只发布创建新任务时,它也会工作。
    4. 从运行命令 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)
    

    日志的事件视图

    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.
    
    1 回复  |  直到 8 年前
        1
  •  2
  •   Blairg23 jfs    8 年前

    看起来问题出在任务的 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()
    
    推荐文章