3

我尝试更新频道:

PUT
content [{'url': 'http://localhost:8000/api/movies/2', 'title': u'Ariel', 'backdrop_path': u'/z2QUexmccqrvw1kDMw3R8TxAh5E.jpg', 'popularity': 0.082, 'poster_path': u'/8ld3BEg8gnynRsfj2AzbLocD8NR.jpg', 'release_date': datetime.date(1988, 10, 21), 'runtime': 69L, 'tagline': u'', 'vote_average': 9.0, 'vote_count': 0L}]
csrfmiddlewaretoken XXXXXXXXXXXXXXXXXXXXXXXXXXx
name    cody private
owner   http://localhost:8000/api/users/1
private 1

我得到这个错误:

instance should be a queryset or other iterable with many=True

这是您需要了解正在发生的事情的代码。

class Channel(models.Model):
    """
    A channel is a "container" for a users movies and television shows.
    """
    PUBLIC_VISIBILITY, PRIVATE_VISIBILITY = 0, 1
    VISIBILITY_CHOICES = (
        (PUBLIC_VISIBILITY, 'public'),
        (PRIVATE_VISIBILITY, 'private'),
    )
    owner = models.ForeignKey(User, related_name='owned_channels')
    name = models.CharField(max_length=60)
    content = models.ManyToManyField(Movie, db_table='channel_contents',
                                     related_name='channels', null=True, blank=True, default=None)
    subscribers = models.ManyToManyField(User, db_table='channel_subscribers',
                                         related_name='subscribed_channels', null=True, blank=True, default=None)
    created = models.DateTimeField(auto_now_add=True)
    last_mod = models.DateTimeField(auto_now=True)
    query = models.CharField(max_length=255, default='')
    private = models.IntegerField(choices=VISIBILITY_CHOICES, default=PRIVATE_VISIBILITY)

    default = models.BooleanField(default=False)


class Movie(models.Model):
    id = models.BigIntegerField(primary_key=True)
    adult = models.BooleanField()
    backdrop_path = models.ImageField(upload_to='backdrop/')
    budget = models.IntegerField(blank=True, null=True)
    genres = models.ManyToManyField('Genre',
                                    through='MovieGenre',
                                    blank=True, null=True)
    homepage = models.URLField(blank=True, null=True)
    imdb_id = models.CharField(max_length=20, blank=True, null=True)
    original_title = models.CharField(max_length=100)
    overview = models.TextField(blank=True, null=True)
    popularity = models.FloatField(blank=True, null=True)
    poster_path = models.ImageField(upload_to='poster/')
    release_date = models.DateField(blank=True, null=True)
    revenue = models.IntegerField(blank=True, null=True)
    runtime = models.IntegerField(blank=True, null=True)
    tagline = models.CharField(max_length=200, blank=True, null=True)
    title = models.CharField(max_length=100, db_index=True)
    vote_average = models.FloatField(blank=True, null=True)
    vote_count = models.IntegerField(blank=True, null=True)
    actors = models.ManyToManyField('Actor',
                                    through='MovieActor',
                                    blank=True, null=True)
    directors = models.ManyToManyField('Director',
                                       through='MovieDirector',
                                       blank=True, null=True)
    production_companies = models.ManyToManyField(
        'ProductionCompany',
        through='MovieProduction',
        blank=True, null=True)

通道序列化代码:

# Routes
url(r'^channels$', ChannelList.as_view(), name='channel-list'),
url(r'^channels/(?P<pk>\d+)$', ChannelDetail.as_view(), name='channel-detail'),

# Views
class ChannelList(generics.ListCreateAPIView):
    """
    API endpoint that represents a list of users.
    """
    model = Channel
    serializer_class = ChannelSerializer

class ChannelDetail(generics.RetrieveUpdateDestroyAPIView):
    """
    API endpoint that represents a single users.
    """
    model = Channel
    serializer_class = ChannelSerializer

# Serializer
class ChannelSerializer(serializers.HyperlinkedModelSerializer):
    content = MovieSerializer(many=True)

    class Meta:
        model = Channel
        fields = ('url', 'owner', 'name', 'content', 'private')
4

3 回答 3

3

如您在此处阅读的,嵌套关系目前不支持写操作。改用HyperlinkedRelatedField或编写自定义序列化程序,以实现您需要的功能。

于 2013-04-24T01:11:21.360 回答
3

如果你想更新嵌套关系,你可以这样做,

      class SchoolSerializer(serializers.HyperlinkedModelSerializer):

                students = StudentSerializer(many=True, read_only=True)
                students_ids = serializers.PrimaryKeyRelatedField(many=True,\
                read_only=False, queryset=Student.objects.all(),\ 
                source='students')

                class Meta:
                    model = School
                    fields = ('name', 'image', 'address', 'url',\
                    'students', 'students_ids')

使用 PrimaryKeyRelatedField 这将允许您创建、更新、嵌套关系(多对多字段),只需传递 id 的学生列表即可为您提供嵌套数据,students_ids 可用于写入操作 DRF 可浏览 api

于 2017-07-07T06:26:26.280 回答
0

这有点过时了,但对于未来寻找此问题潜在解决方案的人来说,我发现修补视图集很有用。

您不能两次读取帖子参数,这是唯一阻止传递相关更新的主键并在 post_save 中执行 m2m 更新的事情

我制作了一个基于 ModelViewSet 的自定义视图集,其中包含更新的创建和更新语句:

在您的应用程序中,您可以创建一个名为 viewsets.py 的模块:

# -*- coding: utf-8 -*-

from rest_framework import mixins
from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

class RelatedCreateModelMixin(mixins.CreateModelMixin):

    '''
    Monkey patch the UpdateModel for ModelViewSet Mixin to support data
    transferrance from pre - to - save - to - post
    '''

    def create(self, request, *args, **kwargs):
        data = request.DATA
        serializer = self.get_serializer(data=data, files=request.FILES)

        if serializer.is_valid():
            self.pre_save(serializer.object, data=data)
            self.object = serializer.save(force_insert=True)
            self.post_save(self.object, created=True, data=data)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED,
                            headers=headers)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class RelatedUpdateModelMixin(mixins.UpdateModelMixin):

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        self.object = self.get_object_or_none()

        data = request.DATA

        serializer = self.get_serializer(self.object, data=data,
                                         files=request.FILES, partial=partial)

        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        try:
            self.pre_save(serializer.object, data=data)
        except ValidationError as err:
            # full_clean on model instance may be called in pre_save,
            # so we have to handle eventual errors.
            return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)

        if self.object is None:
            self.object = serializer.save(force_insert=True)
            self.post_save(self.object, data=data, created=True)
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        self.object = serializer.save(force_update=True)
        self.post_save(self.object, created=False)
        return Response(serializer.data, status=status.HTTP_200_OK)


class RelatedModelViewSet(RelatedCreateModelMixin,
                          mixins.RetrieveModelMixin,
                          RelatedUpdateModelMixin,
                          mixins.DestroyModelMixin,
                          mixins.ListModelMixin,
                          GenericViewSet):
    pass

然后,在您看来,请改用:

from MYAPP import viewsets

这使您可以执行以下操作:

def post_save(self, obj, *args, **kwargs):
    data = kwargs.get('data')
    model_id = data.get('id')
    parent_obj = Model.objects.get(id=model_id)
    method = self.request.method
    if method == 'POST':
        parent_obj.m2m.add(obj)
    elif method == 'PUT':
        parent_obj.m2m.remove(obj)

不是最优雅的解决方案,但我发现编写自定义序列化程序更可取

于 2014-09-09T13:41:08.793 回答