默认情况下,\`.create()\` 方法不支持可写的嵌套字段。

新手上路,请多包涵

关于 DRF 中多对多关系与中间模型的序列化,我有一个大问题:如果请求方法是 get 一切正常。但是,一旦我尝试将数据发布或放置到 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.

我不太确定如何编写正确的创建和更新函数,我也不太理解它在文档中的解释。

代码:

意见.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

序列化程序.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__'

模型.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)

提前致谢。

原文由 nictec 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 873
2 个回答

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 字段传递 pks 列表,例如 [1, 2, 3] 并且为了表示该 mtm 相关模型, EquipmentSerializer 将使用 AssignmentSerializer

原文由 Ivan Semochkin 发布,翻译遵循 CC BY-SA 4.0 许可协议

也许对于大多数遇到同样问题的人来说,这个问题很长。

简短的回答是 DRF 本身不支持嵌套序列化程序的 create 方法。 那么该怎么办?

简单地覆盖默认行为。查看 官方 DRF 文档 中的完整示例

原文由 Juan-Kabbali 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题