#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 , 7 years ago
Cc: | added |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 7 years ago
comment:4 by , 7 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 7 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 , 6 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 , 6 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 , 6 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 , 5 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 , 5 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
F
gets it'srelabeled_clone
from, becauseOuterRef
inherits directly fromF
.