Opened 15 years ago
Closed 10 years ago
#14601 closed Bug (fixed)
ValuesQuerySet join types not being promoted
| Reported by: | ryanbutterfield | Owned by: | nobody | 
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev | 
| Severity: | Normal | Keywords: | ValuesQuerySet promote_alias | 
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | yes | 
| Easy pickings: | no | UI/UX: | no | 
Description
In the following test case the join type of the User model isn't promoted (to a LEFT OUTER JOIN) as it should be when using a ValuesQuerySet. This results in nothing being returned when the Item.person ForeignKey is null.
class User(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Person(models.Model): user = models.ForeignKey(User) class Item(models.Model): person = models.ForeignKey(Person, null=True) amount = models.IntegerField()
To replicate the following is required:
- Three (or more) models deep linked by ForeignKey's.
 
- The first model's (Item) ForeignKey should allow null values.
 
- The query should reference the second model (Person), but not the third (User), before conversion to a ValuesQuerySet.
 
- The values list (that is passed to ValueQuerySet) should reference a field in the third model (User).
 
The following snippet shows what happens currently. Note that the Q objects aren't required, they are just for satisfying point 3 above.
>>> Item.objects.create(person=None, amount=123) <Item: Item object> >>> Item.objects.values('amount', 'person__user__first_name', 'person__user__last_name') [{'amount': 123, 'person__user__last_name': None, 'person__user__first_name': None}] >>> Item.objects.filter(Q(person__isnull=True) | Q(person__isnull=False)) [<Item: Item object>] >>> Item.objects.filter(Q(person__isnull=True) | Q(person__isnull=False)).values('amount', 'person__user__first_name', 'person__user__last_name') []
What happens:
- When the filter is constructed, a query.alias_map entry is created for the Person model. It is NULLABLE so the join type is set to LOUTER which is correct.
 
- During the conversion to a ValuesQuerySet, promote_alias_chain is called on the fields from the User model (eg. person__user__first_name or person__user__last_name).
 
- Because Person had been previously promoted promote_alias returns False which causes promote_alias_chain to not promote the User model.
 
What should happen:
- promote_alias should return True if it promotes the join or if the join has previously been promoted, so that promote_alias_chain can promote the remainder of the chain correctly.
The included diff provides a simple fix to promote_alias and a test case that tests the fix.
Attachments (1)
Change History (8)
by , 15 years ago
| Attachment: | promote_alias.diff added | 
|---|
comment:1 by , 15 years ago
| Patch needs improvement: | set | 
|---|---|
| Triage Stage: | Unreviewed → Accepted | 
Tests need to be updated to be unittests. Preferably, they should also be integrated into an existing test app, rather than starting a whole new test app. The regressiontest/queries test app would seem appropriate.
comment:2 by , 14 years ago
| Severity: | → Normal | 
|---|---|
| Type: | → Bug | 
comment:3 by , 14 years ago
| Easy pickings: | unset | 
|---|---|
| Resolution: | → fixed | 
| Status: | new → closed | 
| UI/UX: | unset | 
This bug still exists. The patch works fine for me, any reason there's been no activity on this for 9 months?
comment:4 by , 14 years ago
| Resolution: | fixed | 
|---|---|
| Status: | closed → reopened | 
Accidently closed, oops. Should anonymous users really have permissions to do that? :/
comment:6 by , 13 years ago
| Status: | reopened → new | 
|---|
comment:7 by , 10 years ago
| Resolution: | → fixed | 
|---|---|
| Status: | new → closed | 
It seems this was fixed in 1.5 as the tests from the patch fail in 1.4 but pass on 1.5 and later.
promote_alias fix and tests