TL; DR - 目前有这样做的任何理智的方式,短创建自定义Serializer
/Deserializer
双。
与具有通用关系模型的问题是,Django不看target
作为现场可言,只有target_content_type
和target_object_id
,并尝试序列化和反序列化单独他们。
负责序列化和反序列化Django模型的类位于模块django.core.serializers.base
和django.core.serializers.python
中。所有其他(xml
,json
和yaml
)扩展其中的任何一个(并且python
延伸base
)。该场序列化是这样的(不相关的线中省略)来完成:
for obj in queryset:
for field in concrete_model._meta.local_fields:
if field.rel is None:
self.handle_field(obj, field)
else:
self.handle_fk_field(obj, field)
这里的第一个并发症:如我们所预期的外键ContentType
被处理好,具有天然的钥匙。但PositiveIntegerField
由handle_field
处理,就是这样实现的:
def handle_field(self, obj, field):
value = field._get_val_from_obj(obj)
# Protected types (i.e., primitives like None, numbers, dates,
# and Decimals) are passed through as is. All other values are
# converted to string first.
if is_protected_type(value):
self._current[field.name] = value
else:
self._current[field.name] = field.value_to_string(obj)
即定制这里唯一的可能性(继承PositiveIntegerField
和定义custom value_to_string
)将没有任何效果,因为串行不会调用它。将target_object_id
的数据类型更改为不是整数的数据类型可能会破坏很多其他的东西,因此它不是一个选项。
我们可以定义我们的自定义handle_field
在这种情况下发出自然键,但随后而来的第二并发症:反序列化完成这样的:
for (field_name, field_value) in six.iteritems(d["fields"]):
field = Model._meta.get_field(field_name)
...
data[field.name] = field.to_python(field_value)
即使我们定制了to_python
方法,仅在对象的上下文中单独作用于field_value
。使用整数不会造成问题,因为它将被解释为模型的主关键字,无论它是什么型号,它都是。但是,要反序列化一个自然键,首先我们需要知道该键属于哪个模型,除非我们获得了对该对象的引用(并且target_content_type
字段已被反序列化),否则该信息不可用。如你所见,这不是一个不可能完成的任务 - 在普通关系中支持自然键 - 但是要完成这个任务,需要在序列化和反序列化代码中改变很多东西。有必要的话(如果有人认为直到任务)的步骤如下:
- 创建
Field
延伸PositiveIntegerField
自定义,使用方法的编码/解码的对象 - 调用参考模型natural_key
和get_by_natural_key
;
- 覆盖串行器的
handle_field
以调用编码器(如果存在);
- 实现一个自定义的反序列化器:1)在字段中强加一些顺序,确保内容类型在自然键之前反序列化; 2)调用解码器,不仅通过
field_value
,而且还参考解码的ContentType
。
我很好奇,你有没有发现这方面的任何解决方案?我做了一些搜索,但没有任何帮助。 – shanyu
还没有,但我会更新此解决方案,如果我找到一个 – Riz
你可以详细说明你的问题吗?一些例子。 – Rohan