I've been reading lately about the generic relations. I know that GenericForeignKey
is to define and manaeg the generic relation using ForeignKey
and PositiveIntegerField
fields. I dove into the source code in search for the __set__
method of the GenericForeignKey
to see how does it work.
Here is the snippet for 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)
and model definition from django docs 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')
When I assign value of guido
to the content_object
then what is the value of each of these paremeters: self
, instance
and value
in the GenericForeignKey.__set__()
?
Is self=<GenericForeignKey: 1>
, instance='content_object'
, and value=<User: guido>
?
>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()
The __set__
method is for descriptors.
The following simple example will show what the arguments passed to __set__
are:
class MyDescriptor:
def __set__(self, instance, value):
print((self, instance, value))
class MyClass:
attr = MyDescriptor()
inst = MyClass()
inst.attr = "foo"
You'll get something like:
<__main__.MyDescriptor object at 0x000002017192AD68>, # self
<__main__.MyClass object at 0x000002017192ACF8>, # instance
'foo' # value
Specifically:
self
is the instance of the MyDescriptor
descriptor (MyClass.attr
), instance
is the instance of the MyClass
class (inst
), andvalue
is what you're setting the attribute to ("foo"
).See a more thorough example here.
So, without similarly diving into the Django code, it would seem that:
self
is the instance of the GenericForeignKey
descriptor (TaggedItem.content_object
), instance
is the instance of the TaggedItem
class, andvalue
is what you're setting the attribute to.But note that, with this line:
t = TaggedItem(content_object=guido, tag='bdfl')
It looks like you're creating a TaggedItem
, which creates a descriptor with this line
content_object = GenericForeignKey('content_type', 'object_id')
So, at least from the code you posted, the __set__
method won't be called. Instead GenericForeignKey
's __init__
method would be called.
To call GenericForeignKey
's __set__
method, you'd need to do have an instance of a class (call it inst
) that had a GenericForeignKey
descriptor as an attribute (call it attr
), then write something like:
inst.attr = "not guido"
Then, the __set__
method of the GenericForeignKey
descriptor would be called.