#29142 closed Bug (fixed)
QuerySet crashes when OuterRef is combined with an operator
| Reported by: | Michael Barr | Owned by: | Matthew Schinckel |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 2.0 |
| Severity: | Normal | Keywords: | |
| Cc: | Matthew Schinckel | Triage Stage: | Ready for checkin |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
According to the documentation on models.OuterRef:
"It acts like an F expression except that the check to see if it refers to a valid field isn’t made until the outer queryset is resolved."
I am experiencing an issue with this using the following example:
class ExampleModel(models.Model):
date = models.DateField()
value = models.IntegerField()
subquery = ExampleModel.objects.filter(
date__gte=models.OuterRef('date') - timedelta(days=2),
date__lte=models.OuterRef('date') + timedelta(days=2),
value__lte=5
)
queryset = ExampleModel.objects.annotate(
value_is_lte_five=models.Exists(subquery)
)
The result that I am getting is:
AttributeError: 'ResolvedOuterRef' object has no attribute 'relabeled_clone'
From my understanding, if this is similar to an F Expression, you should be able to perform the timedelta on the OuterRef. I also found someone else on StackOverflow with the same issue.
What I would like to know is if this is a bug (e.g. should it be supported), or a documentation issue.
Change History (12)
comment:1 by , 8 years ago
| Cc: | added |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 8 years ago
comment:4 by , 8 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:6 by , 8 years ago
| Summary: | OuterRef not being treated as an F Expression → QuerySet crashes when OuterRef is combined with an operator |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
comment:8 by , 7 years ago
| Resolution: | fixed |
|---|---|
| Status: | closed → new |
Issue still exists in django 2.0.5. Performing arithmetic operation on OuterRef returns the following.
'ResolvedOuterRef' object has no attribute 'relabeled_clone'
comment:9 by , 7 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
The patch doesn't qualify for a backport to the stable/2.0.x branch based on our supported versions policy.
comment:10 by , 7 years ago
For anyone needing a backport, it's relatively easy to patch in your userland code.
# Patch from https://github.com/django/django/commit/c412926a2e359afb40738d8177c9f3bef80ee04e # https://code.djangoproject.com/ticket/29142 django.db.models.F.relabeled_clone = lambda self, relabels: self
follow-up: 12 comment:11 by , 6 years ago
The backport does not seem to work for me
class CustomResolvedOuterRef(ResolvedOuterRef):
contains_aggregate = False
def relabeled_clone(self, relabels):
return self
class CustomOuterRef(OuterRef):
def resolve_expression(self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, simple_col=False):
if isinstance(self.name, self.__class__):
return self.name
return CustomResolvedOuterRef(self.name)
queryset.annotate(
title_vector=SearchVector(
'title', config=SIMPLE_PG_SEARCH_CONFIGURATION)
).annotate(
has_role=Exists(
JobTitle.objects.annotate(
title_vector=CustomOuterRef('title_vector')
).filter(
title_vector=SearchQuery(
F('name'), config=SIMPLE_PG_SEARCH_CONFIGURATION)
)
)
)
get this:
Environment:
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/jobs/job/
Django Version: 2.2.2
Python Version: 3.7.3
Installed Applications:
['core',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'django.contrib.postgres',
'allauth',
'allauth.account',
'drf_yasg',
'rest_auth',
'rest_auth.registration',
'rest_framework',
'rest_framework.authtoken',
'colorful',
'gm2m',
'explorer',
'allauth.socialaccount',
'accounts',
'questions',
'colleges',
'geo',
'jobs',
'advices',
'referral']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.7/contextlib.py" in inner
74. return func(*args, **kwds)
File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py" in wrapper
606. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapped_view
142. response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
44. response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/sites.py" in inner
223. return view(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
45. return bound_method(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapped_view
142. response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py" in changelist_view
1672. cl = self.get_changelist_instance(request)
File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py" in get_changelist_instance
744. sortable_by,
File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/views/main.py" in __init__
45. self.root_queryset = model_admin.get_queryset(request)
File "/code/jobs/admin.py" in get_queryset
76. return queryset.annotate_with_has_role()
File "/code/jobs/models/job.py" in annotate_with_has_role
88. F('name'), config=SIMPLE_PG_SEARCH_CONFIGURATION)
File "/usr/local/lib/python3.7/site-packages/django/db/models/query.py" in filter
892. return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/db/models/query.py" in _filter_or_exclude
910. clone.query.add_q(Q(*args, **kwargs))
File "/usr/local/lib/python3.7/site-packages/django/db/models/sql/query.py" in add_q
1290. clause, _ = self._add_q(q_object, self.used_aliases)
File "/usr/local/lib/python3.7/site-packages/django/db/models/sql/query.py" in _add_q
1318. split_subq=split_subq, simple_col=simple_col,
File "/usr/local/lib/python3.7/site-packages/django/db/models/sql/query.py" in build_filter
1207. condition = self.build_lookup(lookups, reffed_expression, value)
File "/usr/local/lib/python3.7/site-packages/django/db/models/sql/query.py" in build_lookup
1104. lookup_class = lhs.get_lookup(lookup_name)
Exception Type: AttributeError at /admin/jobs/job/
Exception Value: 'CustomResolvedOuterRef' object has no attribute 'get_lookup'
comment:12 by , 6 years ago
Replying to Dmitry Mugtasimov:
The backport does not seem to work for me
Does the backport work if you just use the standard OuterRef class? I think perhaps it's something else, as this issue was about the missing 'relabeled_clone', not a missing 'get_lookup'.
Perhaps this is a different issue entirely?
Yeah, I think this is a bug.
I'm not sure where
Fgets it'srelabeled_clonefrom, becauseOuterRefinherits directly fromF.