在 Django 中设置 GenericForeignKey 字段值

Setting GenericForeignKey field value in Django

我最近一直在阅读有关泛型关系的内容。我知道 GenericForeignKey 是使用 ForeignKeyPositiveIntegerField 字段定义和管理通用关系。我深入研究了源代码以搜索 GenericForeignKey__set__ 方法,看看它是如何工作的。

这是 GenericForeignKey.__set__() 的片段:

def __set__(self, instance, value):
    ct = None
    fk = None
    if value is not None:
        ct = self.get_content_type(obj=value)
        fk = value._get_pk_val()
    setattr(instance, self.ct_field, ct)
    setattr(instance, self.fk_field, fk)
    setattr(instance, self.cache_attr, value)

和 django 文档中的模型定义 example:

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

问题:

当我将 guido 的值分配给 content_object 时,这些参数中的每一个的值是什么:selfinstancevalueGenericForeignKey.__set__()?

self=<GenericForeignKey: 1>instance='content_object'value=<User: guido>吗?

>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()

__set__ 方法适用于 descriptors

以下简单示例将显示传递给 __set__ 的参数是什么:

class MyDescriptor:
    def __set__(self, instance, value):
        print((self, instance, value))

class MyClass:
    attr = MyDescriptor()

inst = MyClass()
inst.attr = "foo"

你会得到类似的东西:

<__main__.MyDescriptor object at 0x000002017192AD68>,   # self
<__main__.MyClass object at 0x000002017192ACF8>,        # instance
'foo'                                                   # value

具体来说:

  • selfMyDescriptor描述符的实例(MyClass.attr),
  • instanceMyClass的实例class(inst),而
  • value 是您将属性设置为 ("foo")。

查看更详尽的示例 here

因此,如果不同样深入研究 Django 代码,看起来:

  • selfGenericForeignKey描述符的实例(TaggedItem.content_object),
  • instanceTaggedItem的实例class,而
  • value 是您要设置的属性。

但请注意,这一行:

t = TaggedItem(content_object=guido, tag='bdfl')

看起来您正在创建一个 TaggedItem,它 创建 一个包含此行的描述符

content_object = GenericForeignKey('content_type', 'object_id')

因此,至少从您发布的代码来看,不会调用 __set__ 方法。相反 GenericForeignKey__init__ 方法将被调用。

要调用 GenericForeignKey__set__ 方法,您需要有一个 class 实例(称之为 inst),该实例具有 GenericForeignKey描述符作为一个属性(称之为attr),然后这样写:

inst.attr = "not guido"

然后,GenericForeignKey描述符__set__方法将被调用