Opened 4 years ago
Last modified 3 years ago
#32640 assigned Bug
Non-manager instance assigned to model class' objects is silently ignored
Reported by: | Shai Berger | Owned by: | Shai Berger |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 2.2 |
Severity: | Normal | Keywords: | |
Cc: | Carlton Gibson | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
Consider this models file:
from django.db import models class MyManager(models.Manager): def get_queryset(self): return self.none() class MyModel(models.Model): one = models.IntegerField() objects = MyManager # Note: Missing parentheses
The assignment of a manager class, rather than manager instance, is an easy mistake to make. I know, because I've made it. In a private discussion, Carlton Gibson suggested another variant of this:
class MyOtherModel(models.Model): ... objects = models.Manager.from_queryset(SomeQueryset()) # The above, too, is missing a pair of parentheses
But what should Django do?
There's two possible behaviors I would consider reasonable:
- Trust the user, and rely on Duck Typing. This would blow up very fast in this case, as any method invocation would be missing the
self
argument. - Raise an error on model creation, or at least in
check
.
What Django does instead, is ignore the assignment and use a models.Manager
instance.
Two points to clarify that this is indeed a bug:
- Assigning any object which isn't a
models.Manager
instance -- say, the number 17 -- gets the same result. - This only happens if only one manager is defined. If another manager is defined, the assignment stands as written (the first "reasonable" behavior described above).
I found this on 2.2, it is still this way on master.
Change History (9)
comment:1 by , 4 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 4 years ago
Hmmm. That makes me wonder if the real change we need to make is in add_to_class()
-- make it not override existing attributes so carelessly. That will fix this issue, and maybe some others like it; I only wonder what it will break.
I'll try to make a PR for that later.
comment:3 by , 4 years ago
Has patch: | set |
---|---|
Owner: | changed from | to
Patch needs improvement: | set |
Status: | new → assigned |
We'll see what CI says, but on my machine, the WIP PR only seems to break tests for invalid models, by making them "more invalid" (fail to create the class instead of failing a check).
I'm not sure if that's a bad thing.
comment:4 by , 4 years ago
For the record, CI agrees with my machine. It's the same two tests that fail.
comment:5 by , 4 years ago
Patch needs improvement: | unset |
---|
comment:6 by , 4 years ago
Needs tests: | unset |
---|
comment:7 by , 4 years ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
comment:8 by , 3 years ago
Needs tests: | unset |
---|---|
Patch needs improvement: | unset |
The flags "needs improvement" and "needs tests" were set on this ticket with vague comments on the PR which basically say the same as the flags, with no more details. A request for clarification has been left unanswered for two months. Hence, I'm putting this back on the review queue.
comment:9 by , 3 years ago
Patch needs improvement: | set |
---|
OK, after looking at the PR, I'm going to mark this as Patch needs improvement again. (It may be we uncheck as the discussion on the PR continues.)
In summary, two main points from the review:
- The proposal to
add_to_class()
effects all passes through that, rather than just those where an attribute is added late, and may override an existing attribute. Making the change means we end with different error paths for the case on the concrete instance vs inheritance, and the existing system checks for that seem more tightly targeted for that. - A specific error raised at the point where the default objects manager is added would be able to offer a much better error message, whilst avoiding the above. (A subsidiary System Check could alert to
extra_manager
cases.)
Hi Shai.
I think we might be able to do something here…
If you get into this block where the `objects` manager is automatically added:
…then it seems that any value of
objects
is an error 🤔 — but particularly ifobjects
is aManager
class.I'd think we could at least warn there, but arguably even raise.
Not sure how far down this road it would be worth going.