Opened 13 years ago
Closed 13 years ago
#18595 closed Bug (invalid)
Wrong table used in get_query_set when two Models have ManyToMany Fields to a common third Model using related_name='+'
| Reported by: | Owned by: | nobody | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 1.4 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Summary: Wrong table used in get_query_set when two Models have ManyToMany Fields to a common third Model using related_name='+'
Description:
I discovered this bug after I applied a patch from a previous bug report: #15932
Using this patch:
#15932/15932-1.4rc1-Fixed-model-validation-when-using-hidden-related_name.patch
I have two models that each have an m2m field defiend as: admins = ManyToManyField( User, related_name='+' )
Accessing the first model's "admins" field results in a query being performed on the second model's underlying table.
models.py:
from django.db import models from django.contrib.auth.models import User class Company( models.Model ): name = models.CharField( max_length = 100 ) admins = models.ManyToManyField( User, related_name = '+' ) class Location( models.Model ): company = models.ForeignKey( Company ) address = models.CharField( max_length = 100 ) admins = models.ManyToManyField( User, related_name = '+' )
manage.py shell
In [1]: from testapp.models import User, Company, Location
In [2]: from django.db import connection
In [3]: u = User.objects.get( pk=1 )
In [4]: c = Company( name='Test Co.')
In [5]: c.save()
In [6]: l = Location( company = c, address='123 Test St' )
In [7]: l.save()
In [8]: c.admins.add( u )
In [13]: c.admins.all()
Out[13]: []
In [14]: connection.queries
Out[14]:
[{'sql': "INSERT INTO `testapp_company` (`name`) VALUES ('Test Co.')",
'time': '0.000'},
{'sql': "INSERT INTO `testapp_location` (`company_id`, `address`) VALUES (1, '123 Test St')",
'time': '0.000'},
{'sql': 'SELECT `testapp_company_admins`.`user_id` FROM `testapp_company_admins` WHERE (`testapp_company_admins`.`company_id` = 1 AND `testapp_company_admins`.`user_id` IN (2))',
'time': '0.000'},
{'sql': 'INSERT INTO `testapp_company_admins` (`company_id`, `user_id`) VALUES (1, 2)',
'time': '0.000'},
{'sql': 'SELECT `auth_user`.`id`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`password`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`is_superuser`, `auth_user`.`last_login`, `auth_user`.`date_joined` FROM `auth_user` INNER JOIN `testapp_location_admins` ON (`auth_user`.`id` = `testapp_location_admins`.`user_id`) WHERE `testapp_location_admins`.`location_id` = 1 LIMIT 21',
'time': '0.000'}]
As you can see the query being run has JOIN testapp_location_admins
where it should be testapp_company_admins.
Two possible work-arounds are, either 1) define 'through' models for each
relation, or 2) set related_name='Company_admins+' and
related_name='Location_admins+' respectively.
I figured out the second work-around by digging around the source, where I saw
there are some cases where this kind of related name is generated internally.
And that any related_name ending with '+' gets special treatment
(is_hidden() returns True)
I was able to trace the issue as far as ManyRelatedManager.get_query_set which
is creating a queryset with a join to the wrong table name. As far as I can tell
the manager and the field itself are all referencing the correct tables and
fields, so I am unsure where this is table name confusion is occurring.
Attachments (1)
Change History (5)
comment:1 by , 13 years ago
comment:2 by , 13 years ago
I can't verify this ticket - having two related_name='+' doesn't validate. A small problematic patch attached.
by , 13 years ago
| Attachment: | ticket_18595_tests.diff added |
|---|
comment:3 by , 13 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Accepting per Anssi's comment. See also #15932.
comment:4 by , 13 years ago
| Resolution: | → invalid |
|---|---|
| Status: | new → closed |
Oops - I read "I can verify" instead of "I can't verify".
This ticket reports a bug after applying a patch that wasn't committed in Django yet -- and probably won't. I'm closing it as invalid.
I have also found that it is necessary to use this: related_name = '%(app_label)s_%(class)s_+' on m2m fields in abstract classes as well, you can not just do related_name = '+' or it will use the wrong table name when you try to do o.m2m.all() (or anything that calls ManyRelatedManager.get_query_set)