关于DRF中的中间模型与多对多关系的序列化,我遇到了一个大问题:如果请求方法得到了,那么一切都很完美。但是一旦我尝试POST或PUT Data到API,我就会收到以下错误:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 477, in dispatch
response = self.handle_exception(exc)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 437, in handle_exception
self.raise_uncaught_exception(exc)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 474, in dispatch
response = handler(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/generics.py", line 243, in post
return self.create(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 888, in create
raise_errors_on_nested_writes('create', self, validated_data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 780, in raise_errors_on_nested_writes
class_name=serializer.__class__.__name__
AssertionError: The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `manager.serializers.EquipmentSerializer`, or set `read_only=True` on nested serializer fields.
我不确定如何编写正确的创建和更新函数,我真的不理解它,如何在文档中解释它。
代码:
views.py:
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from manager.serializers import *
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from django.forms.models import model_to_dict
class OrderSetDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
class OrderSetList(generics.ListCreateAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
class EquipmentSetDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
class EquipmentSetList(generics.ListCreateAPIView):
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupSerializer
class ClientList(generics.ListCreateAPIView):
queryset = client.objects.all()
serializer_class = ClientSerializer
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User, Group
from storage.models import *
class AssignmentSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField(source = 'Order.id')
name = serializers.ReadOnlyField(source = 'Order.name')
class Meta:
model = Assignment
fields = ('id', 'name', 'quantity')
class EquipmentSerializer(serializers.ModelSerializer):
event = AssignmentSerializer(source= 'assignment_set', many = True)
class Meta:
model = Equipment
fields = '__all__'
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = client
fields = '__all__'
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name')
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
models.py:
from __future__ import unicode_literals
from django.db import models
from storage.choices import *
# Create your models here.
class Equipment(models.Model):
name = models.CharField(max_length=30)
fabricator = models.CharField(max_length=30, default='-')
storeplace = models.IntegerField()
labor = models.CharField(max_length=1, choices=labor_choices)
event = models.ManyToManyField('Order', blank = True, through= 'Assignment', through_fields=('Equipment', 'Order'))
max_quantity = models.IntegerField(default=1, null = True)
status = models.CharField(max_length=8, choices = STATUS_CHOICES, default = 'im Lager')
def __str__(self):
return self.name
class client(models.Model):
firstname = models.CharField(max_length=30)
secondname = models.CharField(max_length=30)
email = models.EmailField()
post_code = models.IntegerField()
city = models.CharField(max_length=30)
street= models.CharField(max_length=30)
def __str__(self):
return "%s %s" % (self.firstname, self.secondname)
class Order(models.Model):
name = models.CharField(max_length=30)
Type = models.CharField(
max_length=2,
choices=TYPE_CHOICES,
default='Rental',
)
city = models.CharField(max_length=30)
street= models.CharField(max_length=30)
date = models.DateField()
GuestNumber = models.IntegerField()
description = models.TextField()
client = models.ForeignKey("client", on_delete=models.CASCADE, blank = True, null = True)
status = models.CharField(max_length=30, choices=order_choices, default='glyphicon glyphicon-remove')
def __str__(self):
return self.name
class Assignment(models.Model):
Equipment = models.ForeignKey('Equipment', on_delete=models.CASCADE)
Order = models.ForeignKey('Order', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
先谢谢。
答案 0 :(得分:3)
DRF不支持嵌套序列化程序的create
方法。如果要在扩展布局中显示相关字段,而不仅仅是使用pks,则可以覆盖to_representation
方法,而不是重写默认mtm
字段。您应该覆盖create
方法,因为mtm
链接中的第三个模型:
class EquipmentSerializer(serializers.ModelSerializer):
class Meta:
model = Equipment
fields = '__all__'
def create(self, validated_data):
order = Order.objects.get(pk=validated_data.pop('event'))
instance = Equipment.objects.create(**validated_data)
Assignment.objects.create(Order=order, Equipment=instance)
return instance
def to_representation(self, instance):
representation = super(EquipmentSerializer, self).to_representation(instance)
representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data
return representation
现在它将正确传递mtm字段,传递pk列表,例如[1, 2, 3]
,并且为了表示该mtm相关模型,EquipmentSerializer
将使用AssignmentSerializer
。
答案 1 :(得分:2)
对于大多数遇到相同问题的人来说,这个问题可能会很长。
简短的答案是 DRF 不支持嵌套序列化器的本机create
方法。 那该怎么办?
仅覆盖默认行为。在Official DRF docs
中查看完整的示例答案 2 :(得分:0)
我认为错误原因:JSON parse error - No JSON object could be decoded
是因为您忘记将.data
放在@Ivan Semochkin解决方案representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data
的第二行。
因此,我发现我将从行Keyword Error: 'event'
中偶然发现representation = super(EquipmentSerializer, self).to_representation(instance)
,因为EquipmentSeralizer对象包含中介assignment_set
而不是event
。
这是根据我所做的@Ivan Semochkin解决方案改编的最终结果。如果我在实践中错了/不恰当的话,请纠正我。
class EquipmentSerializer(serializers.ModelSerializer):
class Meta:
model = Equipment
fields = '__all__'
def create(self, validated_data):
order = Order.objects.get(pk=validated_data.pop('assignment_set').get('id'))
instance = Equipment.objects.create(**validated_data)
Assignment.objects.create(Order=order, Equipment=Equipment)
return instance
def to_representation(self, instance):
representation = super(EquipmentSerializer, self).to_representation(instance)
representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data
return representation
如果我错了,请纠正我。我是Django的新手。
答案 3 :(得分:0)
我遇到了类似的问题,但使用 update()
方法...
由于此线程,解决方案很简单:https://github.com/beda-software/drf-writable-nested/issues/104..。
我所要做的就是安装库 pip install drf-writable-nested
并导入它:
from drf_writable_nested import WritableNestedModelSerializer
代码应该是这样的:
(归功于:https://github.com/Leonardoperrella)
--serializers.py--
from drf_writable_nested import WritableNestedModelSerializer
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('name', 'code', 'price')
class VendorsSerializer(WritableNestedModelSerializer,
serializers.ModelSerializer):
products = ProductsSerializer(source='vendor', many=True)
class Meta:
model = Vendors
fields = ('name', 'cnpj', 'city', 'products')
--models.py--
class Vendors(models.Model):
name = models.CharField('Name', max_length=50)
cnpj = models.CharField('CNPJ', max_length=14, unique=True, validators=[validate_cnpj])
city = models.CharField('City', max_length=100, blank=True)
class Meta:
verbose_name = "Vendor"
verbose_name_plural = "Vendors"
def __str__(self):
return self.name
class Products(models.Model):
name = models.CharField('Name', max_length=60)
code = models.CharField('Code', max_length=13)
price = models.DecimalField('Price',
max_digits=15,
decimal_places=2,
default=0.00,
validators=[MinValueValidator(Decimal("0.01"))])
vendor = models.ForeignKey('Vendors', on_delete=models.CASCADE, related_name='vendor')
class Meta:
verbose_name = "Product"
verbose_name_plural = "Products"
def __str__(self):
return self.name
答案 4 :(得分:-2)
我认为添加到使用嵌套序列化器属性read_only=True
的序列化器就足够了:
class EquipmentSerializer(serializers.ModelSerializer):
event = AssignmentSerializer(
source='assignment_set',
many = True,
read_only=True) # read_only=True is added here
class Meta:
model = Equipment
fields = '__all__'