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

Sas*_*hua 5 mysql django rest serialization manytomanyfield

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

对我来说,Depot类应该用ManyToManyField表示这种关系似乎很自然.问题是序列化程序似乎不知道如何处理这个ManyToManyField.我尝试了几种解决方法,但每种方法都有自己的错误.我试过让我的DepotSerializer成为a rest_framework.serializers.Serializer或a 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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

此版本在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
Run Code Online (Sandbox Code Playgroud)

这似乎表明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
Run Code Online (Sandbox Code Playgroud)

这给了我错误:

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
Run Code Online (Sandbox Code Playgroud)

经过相当多的调查后,我发现如果在尝试操作该字段之前没有保存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
Run Code Online (Sandbox Code Playgroud)

这确实为此实例成功创建了表条目,但最终会抛出以下错误:

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
Run Code Online (Sandbox Code Playgroud)

在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

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

构建=serializers.PrimaryKeyRelatedField()

因为模型序列化器将为您创建它。事实上,如果您查看文档的示例(http://www.django-rest-framework.org/api-guide/relations/),您会发现当存在 FK 'to' 时,将应用 PrimaryKeyRelatedField当前模型(不是 M2M 关系)。

我会将其从序列化器中删除,然后看看发生了什么。