Opened 14 years ago
Closed 13 years ago
#16173 closed Cleanup/optimization (wontfix)
manager.get on foreign key fields (related fields)
Reported by: | Owned by: | Tobias McNulty | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | manager foreignkey |
Cc: | fhahn | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | yes | UI/UX: | no |
Description (last modified by )
__get__
on ForeignKey field uses manager to query the values.
The code is as given below:
# If the related manager indicates that it should be used for # related fields, respect that. rel_mgr = self.field.rel.to._default_manager db = router.db_for_read(self.field.rel.to, instance=instance) if getattr(rel_mgr, 'use_for_related_fields', False): rel_obj = rel_mgr.using(db).get(**params)
Check the last line rel_obj = rel_mgr.using(db).get(**params)
for using rel_mgr
with multiple databases. rel_mgr.using(db)
returns a queryset. If developer has written a custom manager class for which get
is overriden, then custom managers get
function will never get called. This results in surprises to the developer. I think this line should changed to:
rel_obj = rel_mgr.db_manager(db).get(**params)
Attachments (2)
Change History (11)
comment:1 by , 14 years ago
Description: | modified (diff) |
---|---|
UI/UX: | unset |
comment:2 by , 14 years ago
Easy pickings: | set |
---|---|
Keywords: | manager foreignkey added |
Needs tests: | set |
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
Version: | 1.3 → SVN |
I often find that if I want to provide a custom manager method, I also need a custom queryset... but this does seem reasonable.
comment:3 by , 14 years ago
i tried to proof that and it does not work. maybe the way i'm testing it is wrong :)
comment:4 by , 14 years ago
I think you are testing it wrong. As per my understanding, a1.reporter call in
self.assertRaises(a1.reporter, SuccessfulExecutedException)
will result in a call get function of Reporter's _default_manager and not to get function of ArticleWithDummyManager's default_manager.
comment:5 by , 13 years ago
The test looks like it's going along the right lines. I would suggest to not be so fancy with subclassed models (forcing us all to remember the default manager inheritance rules, which are very tricky). Just make a single model with a relation and explicit default manager. Simpler is better for tests like this so that you're only testing related managers, not related managers and default manager in inherited situtations.
comment:6 by , 13 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:7 by , 13 years ago
Cc: | added |
---|---|
Needs tests: | unset |
I've updated the patch to work with r17591 and the test should work correctly.
But the manager's get uses Manager.get_queryset() now and I'm not sure if returning a Manager instance (rel_mgr.db_manager(db)) instead of a Queryset (rel_mgr.using(db)) is acceptable.
follow-up: 9 comment:8 by , 13 years ago
'But the manager's get uses Manager.get_queryset() now and I'm not sure if returning a Manager instance (rel_mgr.db_manager(db)) instead of a Queryset (rel_mgr.using(db)) is acceptable'.
I am not sure if correctly understood this comment. I am not suggesting any change in Manager.get and of course returning Manager instance instead of queryset will be a wrong decision.
The change is required in get function of class ReverseSingleRelatedObjectDescriptor.
# If the related manager indicates that it should be used for # related fields, respect that. rel_mgr = self.field.rel.to._default_manager db = router.db_for_read(self.field.rel.to, instance=instance) if getattr(rel_mgr, 'use_for_related_fields', False): rel_obj = rel_mgr.using(db).get(**params) else: rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
Check the line,
rel_obj = rel_mgr.using(db).get(**params).
Assume that rel_mgr is a 'custom manager' derived from model.Manager and it overrides get function. In such cases, the custom Manager's derived get function will not get called. Because using(db) will return a queryset while rel_mgr.db_manager(db) will return a 'Manager'. Logically rel_mgr.db_manager(db).get() makes more sense to me. Also I think changes passes all the existing test cases as well. ReverseSingleRelatedObjectDescriptor.get function is still returning an object and there is no change in model.Manager.get() function.
comment:9 by , 13 years ago
Patch needs improvement: | set |
---|---|
Resolution: | → wontfix |
Status: | assigned → closed |
Replying to nitinbhide@…:
Check the line,
rel_obj = rel_mgr.using(db).get(**params).
This line of code no longer exists in trunk Django, the code has been rearranged in a way that makes this change more difficult (ReverseSingleRelatedObjectDescriptor
now has a get_query_set
method, which would have to be changed to get_manager
).
I'm not convinced that it's reasonable to even expect the related manager's get()
method to be called here; get()
is one of the many Manager methods that is simply a pass-through to QuerySet
, and I think it would be a mistake for Django to try to start providing guarantees of when the pass-through Manager method is called vs the same method on the Manager's queryset. If you want to override reliably, you need to override the lower-level get_query_set
method on the Manager and provide a custom QuerySet
class with your get
functionality.
Closing wontfix.
Fixed wiki formatting.