Django模型反序列化与空PK

tef*_*ozi 8 django serialization

我已经序列化了我的django模型:

serializers.serialize(MyModel.objects.filter(color="Red"))
Run Code Online (Sandbox Code Playgroud)

得到这个输出:

<object model="example.example" pk="133">
    <field name="name" type="CharField">John Smith</field>
    <field name="color" type="CharField">Red</field>
    ... #more fields
</object>
Run Code Online (Sandbox Code Playgroud)

所以你可以看到我有pk ="133":

现在我想再次将其反序列化为django模型并将()保存到数据库中,但是使用不同的pk,因此它应该创建具有新id的新记录.

我正在尝试解析XML并使用以下方法更改pk:

  • pk ="" - 解析器抱怨pk应该是整数
  • pk =" - 1"或"0" - 实际创建id/pk ="1"或"0"的记录
  • pk ="None"或None或"null" - 解析器抱怨pk应该是整数
  • 删除"pk"属性 - 解析器抱怨该属性是必需的

在Django Serialization一文中,有一个例子如何使用null"pk"从JSON反序列化.

# You can easily create new objects by deserializing data with an empty PK
# (It's easier to demo this with JSON...)
>>> new_author_json = '[{"pk": null, "model": "serializers.author", "fields": {"name": "Bill"}}]'
>>> for obj in serializers.deserialize("json", new_author_json):
...     obj.save()
Run Code Online (Sandbox Code Playgroud)

(它实际上是0.96,但我认为它应该适用于1.*)

所以在JSON中,pk可以为null,但在XML中它抱怨.如何为XML设置pk为null?

谢谢

tef*_*ozi 5

看起来像Django中的bug.无法为XML序列化对象提供空(或null/None)"pk".

来自django/core/serializers/xml_serializer.py:

class Deserializer(base.Deserializer):
    ...
    def _handle_object(self, node):
    ...
        pk = node.getAttribute("pk")
        if not pk:
            raise base.DeserializationError("<object> node is missing the 'pk' attribute")

        data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
    ...
Run Code Online (Sandbox Code Playgroud)

如果缺少pk属性 - 引发异常.所以我们必须提供一些pk.

来自django/models/fields/init .py

class AutoField(Field):
    ...
    def to_python(self, value):
        if value is None:
            return value
        try:
            return int(value)
        except (TypeError, ValueError):
            raise exceptions.ValidationError(
                _("This value must be an integer."))
    ...
Run Code Online (Sandbox Code Playgroud)

如果pk不是整数 - 也是例外.

看起来没有办法提供空的PK.

解决方法可能是:

  1. 从MyModel获取最大ID
  2. id + = 1
  3. 用新的id在我的xml中设置"pk"
  4. 反序列化为模型
  5. 保存()

这有点棘手,因为在步骤1-5中表应该被锁定..不知何故..只是为了避免id碰撞.

编辑:

解决方法是:

  1. 设置pk ="999999"(一些临时整数值)
  2. 在迭代期间将id和pk设置为None以及稍后save()

    mymodels_iterator = serializers.deserialize("xml",fixed_pk_serialized_xml_model)

    对于mymodels_iterator中的mymodel:

    mymodel.object.id = None
    mymodel.object.pk = None
    mymodel.save()
    
    Run Code Online (Sandbox Code Playgroud)

它的工作原理!

感谢Evgeny关于clone()方法的评论.