使用ManyToManyField序列化模型时出现Django REST错误

时间:2014-08-04 23:16:18

标签: mysql django rest serialization manytomanyfield

我使用Django-REST软件包在Django服务器上创建了一个在软件产品构建中建模一组文件的类。设计是应该能够将文件组(Depot实例)分配给多个Build实例(例如,使用相同的音频文件库的“alpha”和“beta”构建)。但是,在创建库时,它是作为在客户端上创建单个Build的一部分创建的;只是后来,实用程序脚本才允许将现有的Depot添加到其他Build。

我觉得Depot类应该用ManyToManyField表示这种关系。问题是序列化程序似乎不知道如何处理这个ManyToManyField。我尝试了几种解决方法,但每种方法都有自己的错误。我已经尝试将我的DepotSerializer设为rest_framework.serializers.Serializerrest_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版本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'recovery_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条目,ManyToMany关系可能会给您带来麻烦。因此,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 DID帮助中有一个PrimaryKeyRelatedField(many = True)。值得注意的是,我能够使用现有模型创建一个序列化程序实例,并获得正确的serializer.data。但是,我仍然遇到这样的问题:除了创建一个新的模型实例并传递ManyToManyField值之外,restore_object可以执行所有操作。我仍然得到“TypeError:'[PrimaryKeyRelatedField name]'是这个函数的无效关键字参数”如果我将该字段传递给模型的init func。在REST库本身之前,我仍然无法保存模型。此外,在此模式下,序列化程序使用Model的值填充serializer.data,而不是数据输入中提供的值。因此,如果您不在restore_object中使用PrimaryKeyRelatedField的attrs值,则会将其丢弃。

看来我需要将ModelSerializer.save重写为某种预保存,应用ManyToMany输入和后保存,但我需要attrs值,以便我可以在那时应用和修改ManyToManyField 。我意识到串行器确实有init_data字段来查看原始输入,但是在使用序列化程序将数据列表反序列化为新对象列表的情况下,我不知道认为有一种方法可以跟踪哪个serializer.init_data与哪个serializer.object对应。

1 个答案:

答案 0 :(得分:1)

在序列化程序版本1中,您不必添加

builds = serializers.PrimaryKeyRelatedField()

因为模型序列化器会为您创建。事实上,如果你看一下文档的例子(http://www.django-rest-framework.org/api-guide/relations/),你就会发现当有一个FK&#39; to&#39;时会应用PrimaryKeyRelatedField。当前模型(不是M2M关系)。

我会从序列化程序中删除它,然后查看正在进行的操作。