我有以下内容:
target_content_type = models.ForeignKey(ContentType, related_name='target_content_type')
target_object_id = models.PositiveIntegerField()
target = generic.GenericForeignKey('target_content_type', 'target_object_id')
我希望 dumpdata --natural 发出此关系的自然键。这可能吗?如果没有,是否有替代策略不会将我与目标的主键联系起来?
最佳答案
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
字段已经被反序列化),否则该信息不可用)。
正如您所看到的,这并不是一项不可能的任务 - 支持通用关系中的自然键 - 但要实现这一目标,需要在序列化和反序列化代码中更改很多内容。那么必要的步骤(如果有人觉得能够胜任这项任务)是:
- 创建一个扩展
PositiveIntegerField
的自定义Field
,并使用对对象进行编码/解码的方法 - 调用引用模型的natural_key
和get_by_natural_key
; - 覆盖序列化器的
handle_field
以调用编码器(如果存在); - 实现一个自定义反序列化器:1) 在字段中施加某种顺序,确保内容类型在自然键之前被反序列化; 2) 调用解码器,不仅传递
field_value
,还传递对已解码ContentType
的引用。
关于django - 是否可以在 Django 中使用自然键作为 GenericForeignKey ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11159377/