-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
query: process RHS according to its type + tests #106
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d16cc48
to
fc2032b
Compare
`Field.get_db_prep_lookup` merely "prepares" the right hand side operand, and returns a (sql, params) pair. For a plain value `params` will simply be the value itself; compound expressions with `as_sql` method, however, are never designed to work with `get_db_prep_lookup` and instead needs their `as_sql` method called directly to retrieve the pair. The error message has been changed from time to time across Django versions. Related issues include: * disqus#89 "can't adapt type 'CombinedExpression'" in admin filter Since Django 1.10 removed `Field.get_db_prep_lookup`, `Lookup.get_db_prep_lookup` implementation is simply as follows. ```python def get_db_prep_lookup(self, value, connection): return ('%s', [value]) ``` `CombinedExpression` is an expression type returned by combining exprssions with operators, such as `__add__` or `bitor`. They are never meant to be passed to the SQL engine directly; instead, it has an `as_sql` method that returns what we want. `process_rhs` first applies any bilateral transforms on both sides, and if it finds either `as_sql` or `_as_sql` method on the RHS, calls it and wraps the SQL in the pair with parentheses; otherwise, it diverts to the usual `get_db_prep_lookup` as above. * disqus#64 'CombinedExpression' object is not iterable when using an admin filter on Django 1.8 Same as above, except `Lookup.get_db_prep_lookup` is as follows. ```python def get_db_prep_lookup(self, value, connection): return ( '%s', self.lhs.output_field.get_db_prep_lookup( self.lookup_name, value, connection, prepared=True)) ``` Following is the relevant part of `Field.get_db_prep_lookup`. ```python def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): """ Returns field's value prepared for database lookup. """ if not prepared: value = self.get_prep_lookup(lookup_type, value) prepared = True if hasattr(value, 'get_compiler'): value = value.get_compiler(connection=connection) if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'): # If the value has a relabeled_clone method it means the # value will be handled later on. if hasattr(value, 'relabeled_clone'): return value if hasattr(value, 'as_sql'): sql, params = value.as_sql() else: sql, params = value._as_sql(connection=connection) return QueryWrapper(('(%s)' % sql), params) if lookup_type in ('month', 'day', 'week_day', 'hour', 'minute', 'second', 'search', 'regex', 'iregex', 'contains', 'icontains', 'iexact', 'startswith', 'endswith', 'istartswith', 'iendswith'): return [value] ``` Normally it is supposed to return the parameters for the SQL "prepared statement" as a `list`; however, if the value has either `as_sql` or `_as_sql`, it either returns the value directly if it also has `relabeled_clone` (which is the case for expressions) or wraps it in `QueryWrapper`. This is not a desired behavior, and using them would result in `TypeError` saying the object was not iterable since they were not lists in the first place. * disqus#61 'SQLEvaluator' object is not iterable when using admin filter on Django 1.7 Same as above, except `SQLEvaluator` emerges from the SQL compiler wrapping expressions for internal use. - Update expressions: django.db.models.sql.compiler.SQLUpdateCompiler.as_sql: ... if hasattr(val, 'evaluate'): val = SQLEvaluator(val, self.query, allow_joins=False) ... - filter() resolution: django.db.models.sql.query.Query.add_filter: ... elif isinstance(value, ExpressionNode): # If value is a query expression, evaluate it value = SQLEvaluator(value, self, reuse=can_reuse) having_clause = value.contains_aggregate ... This commit also causes "regression" for the following issue; its legitimacy, however, is ambiguous considering that the lookup essentially "hijacks" the original definition of 'exact'. disqus#26 admin BitFieldListFilter not working as expected It should be noted, however, that the original behavior is still possible to achieve by wrapping the `int` in a `BitHandler`. Fixing this correctly would involve treating `int` as `Bit` as before while showing a deprecation warning. Meanwhile `filter(flags=Value(4))` will find a record of which `flags` is exactly `4` across all versions.
fc2032b
to
2d9986e
Compare
This seems like a step forward, merged, thanks @ortholeviator! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Field.get_db_prep_lookup
merely "prepares" the right hand side operand,and returns a (sql, params) pair. For a plain value
params
will simplybe the value itself; compound expressions with
as_sql
method, however,are never designed to work with
get_db_prep_lookup
and instead needstheir
as_sql
method called directly to retrieve the pair.The error message has been changed from time to time across Django
versions. Related issues include:
"can't adapt type 'CombinedExpression'" in admin filter #89 "can't adapt type 'CombinedExpression'" in admin filter
Since Django 1.10 removed
Field.get_db_prep_lookup
,Lookup.get_db_prep_lookup
implementation is simply as follows.CombinedExpression
is an expression type returned by combiningexprssions with operators, such as
__add__
orbitor
.They are never meant to be passed to the SQL engine directly;
instead, it has an
as_sql
method that returns what we want.process_rhs
first applies any bilateral transforms on both sides,and if it finds either
as_sql
or_as_sql
method on the RHS,calls it and wraps the SQL in the pair with parentheses; otherwise,
it diverts to the usual
get_db_prep_lookup
as above.'CombinedExpression' object is not iterable when using an admin filter on Django 1.8 #64 'CombinedExpression' object is not iterable when using an admin filter on Django 1.8
Same as above, except
Lookup.get_db_prep_lookup
is as follows.Following is the relevant part of
Field.get_db_prep_lookup
.Normally it is supposed to return the parameters for the SQL
"prepared statement" as a
list
; however, if the value has eitheras_sql
or_as_sql
, it either returns the value directly if italso has
relabeled_clone
(which is the case for expressions) orwraps it in
QueryWrapper
. This is not a desired behavior, andusing them would result in
TypeError
saying the object was notiterable since they were not lists in the first place.
'SQLEvaluator' object is not iterable when using admin filter on Django 1.7 #61 'SQLEvaluator' object is not iterable when using admin filter on Django 1.7
Same as above, except
SQLEvaluator
emerges from the SQL compilerwrapping expressions for internal use.
Update expressions
django.db.models.sql.compiler.SQLUpdateCompiler.as_sql
:filter() resolution
django.db.models.sql.query.Query.add_filter
:This commit also causes "regression" for the following issue; its
legitimacy, however, is ambiguous considering that the lookup
essentially "hijacks" the original definition of 'exact'.
#26 admin BitFieldListFilter not working as expected
It should be noted, however, that the original behavior is still
possible to achieve by wrapping the
int
in aBitHandler
. Fixingthis correctly would involve treating
int
asBit
as before whileshowing a deprecation warning. Meanwhile
filter(flags=Value(4))
willfind a record of which
flags
is exactly4
across all versions.