#34819: GenericForeignKey.get_prefetch_queryset()
--------------------------------------+------------------------------------
Reporter: Richard Laager | Owner: nobody
Type: Bug | Status: new
Component: contrib.contenttypes | Version: 3.2
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Comment (by Oguzhan Akan):
I was able to reproduce the issue, but am not 100% sure if the suggested
fix is correct.
First let's see the following example:
{{{
# ../tests/generic_relations_regress/models.py
class CustomCharField(models.CharField):
def get_prep_value(self, value):
value = super().get_prep_value(value)
return self.to_python(value) + "-pk"
class Foo1(models.Model):
id = models.CharField(primary_key=True, max_length=25)
class Bar1(models.Model):
content_type = models.ForeignKey(ContentType, models.CASCADE)
object_id = CustomCharField(max_length=25)
content_object = GenericForeignKey()
class Foo2(models.Model):
id = models.CharField(primary_key=True, max_length=25)
class Bar2(models.Model):
content_type = models.ForeignKey(ContentType, models.CASCADE)
object_id = models.CharField(max_length=25)
content_object = GenericForeignKey()
}}}
with the test functions
{{{
# ../tests/generic_relations_regress/tests.py
# ..imports
class GenericRelationTests(TestCase):
def test_custom_prep_value_access_1(self):
foo = Foo1.objects.create(pk="some-test")
bar = Bar1.objects.create(content_object=foo)
self.assertEqual(
foo,
Bar1.objects.prefetch_related("content_object").get(pk=1).content_object,
) # fails
def test_custom_prep_value_access_2(self):
foo = Foo2.objects.create(pk="some-test")
bar = Bar2.objects.create(content_object=foo)
self.assertEqual(
foo,
Bar2.objects.prefetch_related("content_object").get(pk=1).content_object,
) # passes
}}}
From the example above, the first test fails while the second passes.
1. In the first test, `Bar1`'s `object_id` is a `CustomCharField`, which
has its `get_prep_value` function customized.
2. Then, the `content_object` is set to `None` as
`GenericForeignKey.get_prefetch_queryset` function returns empty list for
`ret_val`. This happens because the `get_all_objects_for_this_type`
function
([https://github.com/django/django/blob/a7c73b944f51d6c92ec876fd7e0a171e7c01657d/django/contrib/contenttypes/fields.py#L200
#L200]) uses `fkeys` as the filter. The `fkeys` have the `prep`d values
where the database stores the original strings as the primary key.
3. The suggested solution (running `get_prep_value` in `instance_attr`
function) does not solve the problem as the `prep` function for Foo1 is
different than the Bar1's.
4. In the second test, `Bar2`'s `object_id` is a default `CharField`,
while Foo2 has a `CustomCharField` as its primary key.
5. The test passes as the `GenericForeignKey.get_prefetch_queryset`
returns a non-empty return value.
I believe one should not use a custom field for `object_id` referring a
foreign key. Maybe a warning in docs would suffice? I can work more on
this upon your suggestions.
--
Ticket URL: <https://code.djangoproject.com/ticket/34819#comment:3>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018a84c76936-9023849a-e221-40f0-bc22-7af57871d451-000000%40eu-central-1.amazonses.com.