Opened 13 years ago
Closed 9 years ago
#17143 closed Bug (fixed)
select_related makes base __init__ unsafe
Reported by: | Leo Shklovskii | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
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
For the following models setup:
class A(models.Model): foo = models.CharField(max_length=1, default='N') def __init__(self, *args, **kwargs): super(A, self).__init__(*args, **kwargs) print self.foo class B(A): bar = models.CharField(max_length=1, default='X')
The print
in in class A
is just a proxy for doing something else with the field. My specific use case is that I'm trying to save the field off so I can detect whether it has been changed on save or not - but that doesn't impact the bug.
>>> b = B(foo='Y', bar='Z') Y >>> b.save() >>> A.objects.select_related('b') Y N [<A: A object>]
The second call to A.__init__
is to create the B object, however, it doesn't have all of the fields from the original A so while in A.__init__
, self.foo
evaluates to the wrong value.
This is either something broken with what fields select_related
loads on related objects, or it needs to be documented as a really really nasty gotcha for inherited classes/select_related
.
Attachments (2)
Change History (11)
comment:1 by , 13 years ago
Component: | Database layer (models, ORM) → Documentation |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
comment:2 by , 13 years ago
Component: | Documentation → Database layer (models, ORM) |
---|---|
Type: | Cleanup/optimization → Bug |
I'm not sure I agree, aaugustin. I got further clarification from Leo that this only happens with select_related, not when selecting directly on Bs. IMO that indicates that it is a bug, and might be fixable. I might be wrong; would have to sit down and look at it in more detail to see what a fix would entail. In any case, if Leo or anyone else is interested in looking at a possible fix, I don't want to discourage someone from taking a closer look :-)
comment:3 by , 13 years ago
Indeed, if the problem happens with select_related
but not elsewhere, it could be a bug. This needs a test case, for instance with an assertion on the arguments received in __init__
.
comment:4 by , 13 years ago
I broke this down into a few test conditions and found the report to be accurate. Only through select_related is the default value used, where in all other instantiations of the B the correct value is found.
comment:5 by , 13 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 13 years ago
Has patch: | set |
---|
comment:7 by , 11 years ago
Patch needs improvement: | set |
---|
I'm not sure if this is still an issue, but the test in 17143.diff
passes as far back as I tested (stable/1.3.x) so it doesn't appear to be a proper regression test for the issue.
comment:8 by , 9 years ago
Has patch: | unset |
---|---|
Owner: | removed |
Patch needs improvement: | unset |
Status: | assigned → new |
Based on the test case in the description, it looks like this was fixed in Django 1.6 by 1194a9699932088385f9f88869be28a251597f45. The result is now:
>>> A.objects.select_related('b') Y Y
However, no tests were added in that patch, so it seems a good idea to try to add some before closing this ticket.
comment:9 by , 9 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Actually the tests were added in f51e409a5fb34020e170494320a421503689aea0 -- some didn't pass until the follow up commit.
To the best of my knowledge, that's the expected behavior. I agree the docs could be improved. Basically,
B
carries only thebar
field and a one-to-one relation toA
.