#18416 closed Bug (wontfix)
Query fails when SimpleLazyObject evaluates to None
Reported by: | Bouke Haarsma | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.4 |
Severity: | Normal | Keywords: | |
Cc: | dougvanhorn | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Relevant excerpt from model definition:
class Event(models.Model): season = models.ForeignKey(Season)
Expected result:
>>> Event.objects.filter(season=None) []
Actual result:
>>> from django.utils.functional import SimpleLazyObject >>> lazy = SimpleLazyObject(lambda: None) >>> lazy <django.utils.functional.SimpleLazyObject object at 0x10203cd90> >>> print lazy None >>> Event.objects.filter(season=lazy) Traceback (most recent call last): File "<console>", line 1, in <module> File "django/db/models/manager.py", line 143, in filter return self.get_query_set().filter(*args, **kwargs) File "django/db/models/query.py", line 621, in filter return self._filter_or_exclude(False, *args, **kwargs) File "django/db/models/query.py", line 639, in _filter_or_exclude clone.query.add_q(Q(*args, **kwargs)) File "django/db/models/sql/query.py", line 1250, in add_q can_reuse=used_aliases, force_having=force_having) File "django/db/models/sql/query.py", line 1185, in add_filter connector) File "django/db/models/sql/where.py", line 69, in add value = obj.prepare(lookup_type, value) File "django/db/models/sql/where.py", line 320, in prepare return self.field.get_prep_lookup(lookup_type, value) File "django/db/models/fields/related.py", line 137, in get_prep_lookup return self._pk_trace(value, 'get_prep_lookup', lookup_type) File "django/db/models/fields/related.py", line 210, in _pk_trace v = getattr(field, prep_func)(lookup_type, v, **kwargs) File "django/db/models/fields/__init__.py", line 310, in get_prep_lookup return self.get_prep_value(value) File "django/db/models/fields/__init__.py", line 537, in get_prep_value return int(value) TypeError: int() argument must be a string or a number, not 'SimpleLazyObject'
The SimpleLazyObject
initialization is somewhat more complicated then depicted above, but it can evaluate to None
. However, if it evaluates to None
, it cannot be used as-is in the query and results in the exception as listed above.
Change History (7)
comment:1 by , 12 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
comment:2 by , 12 years ago
And yet, when you accidentally pass None, django gives you that really tall call stack with an unrelated error message at the bottom. Couldn't a better message be issued?
comment:3 by , 12 years ago
I just bumped into this bug. I'm not going to muck with the status, but I wanted to document my thoughts in case anyone else wants to take up the cause on this (I've found it to be a tedious, political process).
The code that raises the exception is this (it's found in most of the Field types):
def get_prep_value(self, value): if value is None: return None return int(value)
It's the value is None
that causes the problem. It's an identity test.
If the intent of the if-test is return None
when the supplied value equates to None
, then the if-test should be re-written as value == None
, which would test for equality. The equality test is slower, though, so you'll likely find argument against using it in this section of the code. Oddly built objects with eq overloaded might also find trouble.
So there may not be much to do here, leaving the burden on the user to figure out what's going on. But I wanted to add some documentation to this bug for future reference.
For what it's worth, I ran into this problem with the following code:
try: agreement = Agreement.objects.get(user=request.user) except Agreement.DoesNotExist, e: agreement = Agreement(user=request.user)
I ended up wrapping the view in the login_required
method, which makes the .user
evaluate to something, preventing the bug. But as stated by bouke, the stack is not very helpful.
comment:4 by , 12 years ago
Cc: | added |
---|
comment:5 by , 12 years ago
Same thing happened to me just now. I believe a common case is request.user
in unit tests. Maybe request.user
shouldn't be a SimpleLazyObject
after all.
comment:6 by , 9 years ago
Also have this issue now. Now with request.user, but with another object (some model instance) I wanted to be evaluated lazyly.
I'm curious how you fixed it? Completely new LazyObject Class, or is there some quick hack?
I guess I will do a quick test for None before I create the lazyobject. I guess it takes another database call, but I don't know how to do it otherwise.
comment:7 by , 7 years ago
I also am curious if anyone came up with a good solution for this. I use my own custom user object, and also a organization object that are both attached to the request in middlewares as SimpleLazyObjects
that have a None
value if they are not logged in or if the logged in user is not part of an organization. Currently attaching to models is a real pain
Action.objects.create( organization=request.organization if request.organization else None, ... )
And what is really bad is that if you forget this and don't test with a user without an organization it can easily go unnoticed!
WONTFIX, I'm afraid. There is nothing we could do apart from putting a special case for
SimpleLazyObject
inget_prep_value()
where theis None
test gets done, which would be horrible.It is Simple Lazy Object after all.