添加了Many2Many现在得到“ValueError:XXX需要有一个字段值id”才能使用这个多对多关系“

时间:2017-09-07 04:06:33

标签: django django-rest-framework

我已经阅读了一堆类似SO的声音问题,但不知怎的,它们都没有帮助我解决我的具体案例。我添加了一个ManyToManyField代表“喜欢”到我的Photo模型:

class Photo(TimestampModerated):
    owner = models.ForeignKey('auth.User', related_name='photos', on_delete=models.CASCADE)
    uuid = models.UUIDField(default=uuid4, editable=False)
    slug = models.SlugField(max_length=80, editable=False)
    title = models.CharField(max_length=80, blank=False, null=False)
    description = models.TextField(verbose_name='description of entity', blank=True, null=True)
    photo = models.ImageField(upload_to=user_directory_path, height_field="height", width_field="width", blank=True)
    height = models.IntegerField(blank=True)
    width = models.IntegerField(blank=True)
    tags = TaggableManager(blank=True)
    hash = models.CharField(max_length=64, unique=True, null=True)
    size = models.BigIntegerField('size of file in bytes', blank=True, null=True)
    likes = models.ManyToManyField('auth.User', blank=True, null=True)

    class Meta:
        verbose_name_plural = "photos"

    def __str__(self):
        return self.title

    def delete(self, using=None, keep_parents=False):
        default_storage.delete("{}".format(self.photo))
        super().delete()

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(Photo, self).save(*args, **kwargs)

以下是视图(至少是创建的重要部分):

class PhotoViewSet(viewsets.ModelViewSet):
    queryset = Photo.objects.all()
    serializer_class = PhotoSerializer
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsOwnerOrReadOnly, permissions.AllowAny,)
    parser_classes = (MultiPartParser, FormParser)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user, likes=[])

这是序列化器:

class PhotoSerializer(serializers.ModelSerializer, TaggitSerializer):
    owner = serializers.CharField(source='owner.username', read_only=True)
    tags = TagListSerializerField()
    photo = Base64ImageField(
        max_length=None, use_url=True,
    )

    class Meta:
        model = Photo
        fields = ('photo', 'height', 'width', 'owner', 'slug', 'uuid', 'title', 'id', 'created', 'updated',
                  'moderation_code', 'tags', 'hash', 'description', 'size', 'likes')

    def create(self, validated_data):
        photo = Photo.objects.create(owner=validated_data.pop('owner'),
                                     **validated_data)
        p = Photo.objects.get(uuid=photo.uuid)
        [p.tags.add(tag) for tag in validated_data['tags']]
        return photo

要清楚,唯一添加的是模型中的likes字段。此外,我应该注意到,我能够在迁移之前将新的喜欢发布到已经存在于数据库中的照片。我只是在创建新实例时遇到问题。有什么想法吗?

以下是完整的错误追溯:

Internal Server Error: /api/photos/
Traceback (most recent call last):
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/viewsets.py", line 86, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/views.py", line 489, in dispatch
    response = self.handle_exception(exc)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/views.py", line 449, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/views.py", line 486, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/mixins.py", line 21, in create
    self.perform_create(serializer)
  File "/Users/xxxxxx/_/photos/views.py", line 24, in perform_create
    serializer.save(owner=self.request.user, likes=[])
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/rest_framework/serializers.py", line 215, in save
    self.instance = self.create(validated_data)
  File "/Users/xxxxxx/_/photos/serializers.py", line 88, in create
    **validated_data)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/query.py", line 392, in create
    obj = self.model(**kwargs)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/base.py", line 566, in __init__
    _setattr(self, prop, kwargs[prop])
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 536, in __set__
    manager = self.__get__(instance)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 513, in __get__
    return self.related_manager_cls(instance)
  File "/Users/xxxxxx/_/.virtualenv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 830, in __init__
    (instance, self.pk_field_names[self.source_field_name]))
ValueError: "<Photo: photo-260561.jpeg>" needs to have a value for field "id" before this many-to-many relationship can be used.

1 个答案:

答案 0 :(得分:0)

好吧,我用一个看似非常简单的代码解决了这个问题:

def create(self, validated_data):
    validated_data.pop('likes') ## adding this line solved the issue 
    photo = Photo.objects.create(owner=validated_data.pop('owner'),
                                 **validated_data)
    p = Photo.objects.get(uuid=photo.uuid)
    [p.tags.add(tag) for tag in validated_data['tags']]
    return photo

如果有人能解释为什么会这样,我有兴趣知道。我应该指出,对于这个用例,照片永远不会用喜欢的方式启动。