django - 是否可以在 Django 中使用自然键作为 GenericForeignKey ?

标签 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')

我希望 dumpdata --natural 发出此关系的自然键。这可能吗?如果没有,是否有替代策略不会将我与目标的主键联系起来?

最佳答案

TL;DR - 目前没有明智的方法可以做到这一点,除了创建自定义Serializer/Deserializer对。

具有通用关系的模型的问题是 Django 根本不将 target 视为字段,只有 target_content_typetarget_object_id ,并尝试单独序列化和反序列化它们。

负责序列化和反序列化 Django 模型的类位于模块 django.core.serializers.base 中和 django.core.serializers.python 。所有其他(xmljsonyaml)都扩展了其中一个(并且 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 的外键处理正常,使用我们期望的自然键。但是 PositiveIntegerFieldhandle_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/

相关文章:

sql - 关系数据库设计问题 - 代理键还是自然键?

sql - 代理键 'preference' 解释

django - GenericForeignKey、ContentType 和 DjangoRestFramework

Django 通用外键过滤(v1.5 和 v1.6 之间的差异)

django - 将表单添加到 django-flatpages

python - zope.testbrowser 的代理设置

django - 如何使用 allauth 和 rest-auth 从 React 前端在 Django 中重新发送确认电子邮件

python manage.py runserver 没有名为 django.core.management 的模块

java - Hibernate:带有继承的 NaturalId

django:如何根据 GenericForeignKey 的字段进行查询?