2013-04-23 74 views
3

我尝试更新的频道:更新M2M关系Django的REST框架(多=真)

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') 

回答

3

正如你可以阅读here,嵌套关系目前不支持写入操作。改为使用HyperlinkedRelatedField或编写一个自定义序列化程序,实现您所需的功能。

+1

我没有使用hyperlinkedRelatedField,但我遇到了那个花了相当一段时间来调试的问题。永久加载api的html页面。原因是它正在加载一个包含相关“电影”的选择框,即30万个元素。 – Codygman 2013-04-26 20:58:38

0

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

您无法读取post数据的两倍,这是防止一个从通过主键的相关更新和执行M2M更新的post_save

我做了基于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) 

不是最完美的解决方案,但我觉得它优于编写自定义序列

1

如果要更新嵌套关系,你可以像这样,

 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这将允许您创建,更新,嵌套关系(很多到许多领域)用j UST传递ID的 学生名单会给你嵌套数据, students_ids可用于写操作 DRF Browsable api