#34178: Prefetching a foreign key on GenericForeignKey results in incorrect
queryset being selected
-------------------------------------+-------------------------------------
Reporter: Rohan Nahata | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 4.1
(models, ORM) |
Severity: Normal | Resolution:
Keywords: GenericForeignKey, | Triage Stage:
prefetch_related | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Rohan Nahata:
Old description:
> These are the models that I created to test my hypothesis. .
> {{{
> class Foreign(models.Model):
> value = models.CharField(max_length=20)
>
> class OtherForeign(models.Model):
> value = models.CharField(max_length=20)
>
> class A(models.Model):
> f = models.ForeignKey(Foreign, on_delete=models.PROTECT)
>
> class B(models.Model):
> f = models.ForeignKey(OtherForeign, on_delete=models.PROTECT)
>
> class X(models.Model):
> ct = models.ForeignKey(ContentType, on_delete=models.PROTECT)
> object_id = models.IntegerField()
> object = GenericForeignKey(ct_field='ct', fk_field='object_id')
> }}}
>
> The tables were populated with this sample data.
> {{{
> Foreign
>
> id|value
> 1|A1
> 2|A2
> 3|A3
> 4|A4
> 5|A5
> 6|A6
> 7|A7
> 8|A8
> 9|A9
> 10|A10
>
> Other Foreign
>
> id|value
> 1|B1
> 2|B2
> 3|B3
> 4|B4
> 5|B5
> 6|B6
> 7|B7
> 8|B8
> 9|B9
> 10|B10
>
> A
>
> id|f_id
> 1|1
> 2|2
> 3|3
> 4|4
> 5|5
> 6|6
> 7|7
> 8|8
> 9|9
> 10|10
>
> B
>
> id|f_id
> 1|1
> 2|2
> 3|3
> 4|4
> 5|5
> 6|6
> 7|7
> 8|8
> 9|9
> 10|10
>
> X
> id|object_id|ct_id
> 1|1|11
> 2|2|11
> 3|3|11
> 4|4|11
> 5|5|11
> 6|6|11
> 7|7|11
> 8|8|11
> 9|9|11
> 10|10|11
> 11|1|10
> 12|2|10
> 13|3|10
> 14|4|10
> 15|5|10
> 16|6|10
> 17|7|10
> 18|8|10
> 19|9|10
> 20|10|10
>
> }}}
>
> Based on this data :
>
> {{{
> ->> xx = X.objects.prefetch_related('object').all().order_by('id')
> ->> xx[0].object.f.value
> 'A1'
> ->> xx[10].object.f.value
> 'B1'
> }}}
> This behaviour is correct.
>
> However, when we chain another prefetch to the same,
> {{{
> ->> xx = X.objects.prefetch_related('object',
> 'object__f').all().order_by('id')
> ->> xx[0].object.f.value
> 'A1'
> ->> xx[10].object.f.value
> 'A1'
> }}}
> Here, {{{xx[10].object}}} returns the correct object, however, when
> {{{xx[10].object.f}}} is evaluated it refers to model A instead of model
> B. This leads me to believe that there is some sort of faulty caching
> going on in the background that leads to model A being used.
New description:
These are the models that I created to test my hypothesis. .
{{{
class Foreign(models.Model):
value = models.CharField(max_length=20)
class OtherForeign(models.Model):
value = models.CharField(max_length=20)
class A(models.Model):
f = models.ForeignKey(Foreign, on_delete=models.PROTECT)
class B(models.Model):
f = models.ForeignKey(OtherForeign, on_delete=models.PROTECT)
class X(models.Model):
ct = models.ForeignKey(ContentType, on_delete=models.PROTECT)
object_id = models.IntegerField()
object = GenericForeignKey(ct_field='ct', fk_field='object_id')
}}}
The tables were populated with this sample data.
{{{
Foreign
id|value
1|A1
2|A2
3|A3
4|A4
5|A5
6|A6
7|A7
8|A8
9|A9
10|A10
Other Foreign
id|value
1|B1
2|B2
3|B3
4|B4
5|B5
6|B6
7|B7
8|B8
9|B9
10|B10
A
id|f_id
1|1
2|2
3|3
4|4
5|5
6|6
7|7
8|8
9|9
10|10
B
id|f_id
1|1
2|2
3|3
4|4
5|5
6|6
7|7
8|8
9|9
10|10
X
id|object_id|ct_id
1|1|11
2|2|11
3|3|11
4|4|11
5|5|11
6|6|11
7|7|11
8|8|11
9|9|11
10|10|11
11|1|10
12|2|10
13|3|10
14|4|10
15|5|10
16|6|10
17|7|10
18|8|10
19|9|10
20|10|10
}}}
Based on this data :
{{{
->> xx = X.objects.prefetch_related('object').all().order_by('id')
->> xx[0].object.f.value
'A1'
->> xx[10].object.f.value
'B1'
}}}
This behaviour is correct.
However, when we chain another prefetch to the same,
{{{
->> xx = X.objects.prefetch_related('object',
'object__f').all().order_by('id')
->> xx[0].object.f.value
'A1'
->> xx[10].object.f.value
'A1'
}}}
Here, {{{xx[10].object}}} returns the correct object, however, when
{{{xx[10].object.f}}} is evaluated it refers to model A instead of model
B. This leads me to believe that there is some sort of faulty caching
going on in the background that leads to model A being used.
This bug was tested on 4.1.3, 3.2.16, 2.2.28 and was reproducible.
--
--
Ticket URL: <https://code.djangoproject.com/ticket/34178#comment:1>
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/010701849f3332a4-fc636238-d56c-414b-b624-59b7a51515ff-000000%40eu-central-1.amazonses.com.