Opened 3 years ago
Last modified 2 years ago
#33450 assigned Bug
Integer primary key is wrongly casted to UUID when filtering GenericRelation on model with UUID primary key.
Reported by: | Santos Gallegos | Owned by: | Abhinav Yadav |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.0 |
Severity: | Normal | Keywords: | |
Cc: | Timur, Chinmoy | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | yes | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
Hi, at Read the Docs we have migrated from Django 2.2 to 3.2, and we encountered the following problem. In a model that makes use of a GenericForeignKey field, and it has a UUID field as its pk will result in Django casting the ID of the related model as UUID in some queries (the related model makes use of the default BigAutoField). I was able to reproduce this with the following models and using postgres as the DB backend.
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) import uuid class TTag(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content = GenericForeignKey() class TIntegration(models.Model): tags = GenericRelation(TTag, related_query_name='integration')
The problematic queries:
integration = TIntegration.objects.create() tag = integration.tags.create() r = TTag.objects.filter(integration=integration) print(r.query) # The next error is raised if the query is evaluated # psycopg2.errors.UndefinedFunction: operator does not exist: bigint = uuid
The query will result in the following SQL
SELECT "core_ttag"."id", "core_ttag"."content_type_id", "core_ttag"."object_id" FROM "core_ttag" INNER JOIN "core_tintegration" ON ("core_ttag"."object_id" = "core_tintegration"."id" AND ("core_ttag"."content_type_id" = 7)) WHERE "core_tintegration"."id" = 00000000-0000-0000-0000-000000000001
Note as core_tintegration"."id"
is being casted as an UUID when it should be an integer (1). I was able to bisect the error to this commit https://github.com/django/django/commit/1afbc96a75bd1765a56054f57ea2d4b238af3f4d. Before that commit, the generated SQL is as follows:
SELECT "core_ttag"."id", "core_ttag"."content_type_id", "core_ttag"."object_id" FROM "core_ttag" INNER JOIN "core_tintegration" ON ("core_ttag"."object_id" = "core_tintegration"."id" AND ("core_ttag"."content_type_id" = 7)) WHERE "core_tintegration"."id" = 1
We were able to work around this issue by changing the queries to explicitly use the ID.
TTag.objects.filter(integration__id=integration.id)
Change History (9)
comment:1 by , 3 years ago
Cc: | added |
---|---|
Summary: | Integer PK wrongly casted as UUID when using with a GenericForeignKey model with a UUID field → Integer primary key is wrongly casted to UUID when filtering GenericRelation on model with UUID primary key. |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 3 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:3 by , 3 years ago
Cc: | added |
---|
comment:4 by , 3 years ago
Upon further investigation in Santos Gallegos original post, I also found that in RelatedLookupMixin.get_prep_lookup it also part of the issue since "target_field = self.lhs.output_field.path_infos[-1].fields[-1]" points at TTag's primary key which is a UUID instead of TIntegration's ID. Was able to resolve the issue by using reverse_path_infos but unsure if this is correct. So the issue is not so much related to https://github.com/django/django/commit/1afbc96a75bd1765a56054f57ea2d4b238af3f4d which does the casting but that at this point of the code it thinks it should get the ID value for TTag instead of TIntegration.
I will try to see if I can resolve the issue but wanted to share what I found so far. Let me know what you guys think.
comment:5 by , 3 years ago
Has patch: | set |
---|---|
Triage Stage: | Accepted → Ready for checkin |
Here is the pull request https://github.com/django/django/pull/15397
comment:6 by , 3 years ago
Triage Stage: | Ready for checkin → Accepted |
---|
Thanks for submitting the PR. RFC is a status set by a reviewer.
comment:7 by , 3 years ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
comment:8 by , 2 years ago
Owner: | changed from | to
---|
comment:9 by , 2 years ago
I have been working on this issue for a while, trying to understand it, but I'm unable to figure out how django is working internally to answer a question raised by Mariusz Felisiak in this comment asking why self.lhs.output_field
points generic_relations.TIntegration.tags
and not to generic_relations.TIntegration
. I tried debugging and checking the variables in related_lookups.py but to no avail.
Any suggestions on where to proceed so as to fix this bug? The patch provided for this issue seems to work by reversing the fields but I suppose it's not the expected behaviour to begin with?
Thanks for the report! Regression in 1afbc96a75bd1765a56054f57ea2d4b238af3f4d.
While checking this report I found another issue that's related but it's not a regression:
so cast to
uuid
is missing here 🤔.