我在Django服务器上使用Django- rest包创建了一个类,对软件产品构建中的一组文件进行建模。设计是文件组(仓库实例)应该能够分配给多个构建实例(例如,"alpha"one_answers"beta"构建使用相同的音频文件仓库)。然而,当仓库被创建的时候,它是作为客户端单次构建的一部分被创建的;只有在后来,一个实用程序脚本才允许将现有的仓库添加到其他构建中。
对我来说,Depot类应该用ManyToManyField表示这种关系,这似乎是很自然的。问题是序列化器似乎不知道如何处理这个ManyToManyField。我尝试了几种解决方法,但每种方法都有自己的错误。我试过让我的DepotSerializer是rest_framework.serializers.Serializer
或rest_framework.serializers.ModelSerializer
,但这似乎在很大程度上与这个问题无关。
Models.py:
class Depot(models.Model):
name = models.CharField(max_length=64)
builds = models.ManyToManyField(Build)
TYPE_EXECUTABLE = 0
TYPE_CORE = 1
TYPE_STREAMING = 2
depot_type = models.IntegerField(choices = (
(TYPE_EXECUTABLE, 'Executable'),
(TYPE_CORE, 'Core'),
(TYPE_STREAMING, 'Streaming'),
))
def __str__(self):
return self.name
Views.py:
class DepotCreate(mixins.CreateModelMixin,
generics.GenericAPIView):
serializer_class = DepotSerializer
queryset = Depot.objects.all()
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Serializers.py version 1:
class DepotSerializer(serializers.ModelSerializer):
builds = serializers.PrimaryKeyRelatedField()
class Meta:
model = Depot
fields = ('id', 'name', 'builds', 'depot_type')
read_only_fields = ('id',)
def validate(self, attrs):
build = attrs['builds']
if build == None:
raise serializers.ValidationError("Build could not be found")
for depot in build.depot_set.all():
if depot.name == attrs['name']:
raise serializers.ValidationError("Build already contains a depot "{}"".format(depot.name))
return attrs
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
return Depot(**attrs)
此版本在Depot init调用期间导致以下错误:
Exception Type: TypeError
Exception Value:
'builds' is an invalid keyword argument for this function
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/django/db/models/base.py in __init__, line 417
这似乎表明Depot模型不能处理'builds'参数,尽管它有一个'builds' ManyToManyField成员。
Serializers.py 'restore_object' ver 2:
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
build = attrs['builds']
depotObj = Depot(name=attrs['name'], depot_type=attrs['depot_type'])
depotObj.builds.add(build)
return depotObj
这给了我错误:
Exception Type: ValueError
Exception Value:
"<Depot: depot_test4>" needs to have a value for field "depot" before this many-to-many relationship can be used.
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/django/db/models/fields/related.py in __init__, line 524
经过相当多的调查后,我发现如果在试图操作该字段之前不保存MYSQL条目,那么manymany关系会给您带来麻烦。因此,restore_object ver 3:
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
build = attrs['builds']
depotObj = Depot(name=attrs['name'], depot_type=attrs['depot_type'])
depotObj.save()
depotObj.builds.add(build)
return depotObj
为这个实例成功地创建了表项,但最终抛出了以下错误:
Exception Type: IntegrityError
Exception Value:
(1062, "Duplicate entry '5' for key 'PRIMARY'")
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/MySQLdb/connections.py in defaulterrorhandler, line 38
此错误发生在rest_framework/mixins.py调用serializer.save(force_insert=True)期间。这看起来像是要强制创建一个新的表项,可能与我之前对Model.save的调用不一致。
有谁知道这样设计的正确方法吗?我觉得这不是一个不寻常的表结构。
编辑10/20/2014:根据下面的建议,我尝试为我的一个模型编写一个新的ModelSerializer;在很大程度上,由于这些类型的操作顺序问题,我放弃了使用ModelSerializer,并通过读取serializer.data在views.py中完成了所有数据到对象的字段处理。
在ModelSerializer中使用PrimaryKeyRelatedField(many=True)确实有帮助。值得注意的是,我能够使用现有的模型创建序列化器实例,并获得正确的serializer.data。然而,我仍然有问题,其中restore_object可以做任何事情,除了创建一个新的模型实例并传递ManyToManyField值。我仍然得到"TypeError: '[PrimaryKeyRelatedField名称]'是此函数的无效关键字参数",如果我将该字段传递给模型的init函数。在REST库自己保存模型之前,我仍然不能保存模型。此外,在此模式下,序列化程序填充序列化程序。数据与模型的值,而不是数据输入中提供的值。因此,如果在restore_object中不使用PrimaryKeyRelatedField的attrs值,它将被丢弃。
看来我需要重写ModelSerializer。保存到某种形式的预保存,应用manymany输入,和一个后保存,但我需要attrs值,所以我可以应用和修改ManyToManyField。我意识到序列化器确实有init_data字段来查看原始输入,但是在使用序列化器将数据列表反序列化为新对象列表的情况下,我认为没有办法跟踪哪个序列化器。Init_data对应serializer.object
在序列化器版本1中,不需要添加
builds = serializers.PrimaryKeyRelatedField()
作为模型序列化器将为您创建这个。事实上,如果您查看文档的示例(http://www.django-rest-framework.org/api-guide/relations/),您将看到PrimaryKeyRelatedField在存在FK 'to'当前模型(而不是M2M关系)时被应用。
我将把它从序列化器中删除,然后看看发生了什么