是否可以在Django中使用GenericForeignKey的自然键?

Riz*_*Riz 8 django natural-key generic-foreign-key

我有以下内容:

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

我想dumpdata --natural为这种关系发出一个自然的关键.这可能吗?如果没有,是否有一种替代策略不会将我绑定到目标的主键?

mgi*_*nbr 7

TL; DR - 目前没有明智的做法,没有创建自定义Serializer/ Deserializer对.

与具有通用关系模型的问题是,Django不看target可言,只能作为一个字段target_content_typetarget_object_id,并尝试序列化和反序列化单独他们.

负责序列化和反序列化Django模型中的类的模块django.core.serializers.basedjango.core.serializers.python.所有其他(xml,jsonyaml)扩展其中任何一个(和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)
Run Code Online (Sandbox Code Playgroud)

这是第一个复杂问题: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)
Run Code Online (Sandbox Code Playgroud)

即,这里定制的唯一可能性(子类化PositiveIntegerField和定义自定义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)
Run Code Online (Sandbox Code Playgroud)

即使我们自定义了该to_python方法,它也会field_value在对象的上下文中单独作用.使用整数时不是问题,因为它将被解释为模型的主键,无论它是什么模型.但是为了反序列化一个自然键,首先我们需要知道该键属于哪个模型,并且除非我们获得对该对象的引用(并且该target_content_type字段已被反序列化),否则该信息不可用.

正如您所看到的,这不是一项不可能完成的任务 - 支持泛型关系中的自然键 - 但要实现这一点,需要在序列化和反序列化代码中更改许多内容.必要的步骤(如果有人感觉到任务)是:

  • 使用编码/解码对象的方法创建自定义Field扩展PositiveIntegerField- 调用引用的模型' natural_keyget_by_natural_key;
  • 覆盖串行handle_field器以调用编码器(如果存在);
  • 实现一个自定义反序列化器:1)在字段中强加一些顺序,确保在自然键之前反序列化内容类型; 2)调用解码器,不仅传递field_value而且还传递解码的参考ContentType.