我尝试更新频道:
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')
答案 0 :(得分:3)
正如您可以阅读here,嵌套关系当前不支持写操作。请改用HyperlinkedRelatedField
或编写自定义序列化程序,以实现您需要的功能。
答案 1 :(得分:2)
如果您想更新嵌套关系,可以这样做,
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可用于写操作
答案 2 :(得分:0)
这有点过时,但是对于寻找这个问题的潜在解决方案的未来人们,我发现修补视图集很有用。
你不能两次阅读post params,这是阻止一个人传递相关更新的主键并在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)
不是最优雅的解决方案,但我发现它更适合编写自定义序列化程序