The parens definitely would work for __neg__, and as for the output_type
problem, it's only an issue when mixing field types - but that can be
solved by applying a matching output_type to one of the inner expressions.
If a wrapping type isn't needed for neg, I won't introduce one to slightly
improve the aesthetics of combinable expressions.
To answer your question about using F() as a wrapper - no that wouldn't
work. F() must refer to an existing field by name - it doesn't wrap
expressions.
Josh
On Monday, 9 June 2014 20:34:04 UTC+10, Anssi Kääriäinen wrote:
>
> Could we use just F() for this, that is the examples would be:
> -F(F('field')+F('other'))
> F(F('field')+5, output_type=IntegerField())
>
> As Marc said in another post plain parentheses should be enough for the
> first case.
>
> - Anssi
>
> On 06/09/2014 01:19 PM, Josh Smeaton wrote:
>
> If __neg__ is the preferred method of applying ordering then I think we
> should prevent __neg__ from being used in the mathematical sense in the
> other methods (filter, annotate etc). There hasn't been a need for negating
> an expression/F() object before, and negation would only work with certain
> expressions anyway (what would -Lower('description') even mean?).
>
> The problem with applying ordering to an expression still applies:
>
> Model.objects.all().order_by( F('field')+F('other') )
>
>
> Here, it's actually the connector (+) that is the outer-most level, so
> there's no opportunity to apply ordering to any of the expressions. I've
> found a similar problem with trying to apply an `output_type` to
> expressions that are joined by a connector. I think the solution is to
> introduce a new type that simply wraps another expression for the purposes
> of mutation.
>
> Model.objects.all().order_by( -Wrap(F('field')+F('other')) )
>
> Model.objects.annotate( Wrap(F('field')+5, output_type=IntegerField()) )
>
>
> But I'd be interested in hearing alternatives.
>
> Josh
>
> On Monday, 9 June 2014 20:01:18 UTC+10, Anssi Kääriäinen wrote:
>>
>> Using __neg__ for ordering seems like a good approach to me. I don't
>> like F('-field') that much, if -F('field') works, then F('-field') is
>> duplicate API for the exact same thing.
>>
>> The only problem with -F('field') is that there might be situations where
>> ORDER BY -field ASC gives different results than ORDER BY field DESC. I am
>> not sure if any such thing exists (maybe null handling?), but if so, and if
>> we also add support for __neq__ for somecol__lt=-F('field'), then users
>> might get unexpected results from .order_by(-F('field')). OTOH if we
>> document that '-' means DESC ordering when used at outermost level in
>> .order_by(), the API is hopefully clear enough.
>>
>> - Anssi
>>
>> On 06/09/2014 09:33 AM, Marc Tamlyn wrote:
>>
>> Agreed. It's worth noting that we could have a `Desc` operator for
>> awkward edge cases where __neg__ doesn't work well, and the default
>> implementation of __neg__ just returns Desc(self). I've not completely
>> considered all the possible implications here, just throwing the idea
>> around.
>>
>>
>> On 9 June 2014 01:53, Josh Smeaton <[email protected]> wrote:
>>
>>> > That sounds like a workable approach. If I'm understanding right,
>>> this means putting the ordering flag on the ExpressionNode class so that
>>> all nodes will have this whether or not they are to be used in an ordering
>>> context?
>>>
>>> Yes, that had been what I was thinking, based upon the implementation
>>> of PeeWee (
>>> https://github.com/coleifer/peewee/blob/master/peewee.py#L292 ). At
>>> first it doesn't seem very nice; having an expression containing properties
>>> regarding ordering or even aggregates (like the current is_aggregate). But
>>> in my experience it has turned out to be the nicest place to put that
>>> information, that doesn't infect a large number of components. "An
>>> expression can be ordered" makes sense.
>>>
>>> > My only reservation about this when combined with __neg__ is that it
>>> means that certain mathematical expressions will behave oddly
>>>
>>> __neg__ isn't currently supported on expressions, so it has no current
>>> meaning. It could be used in the mathematical context though in the future,
>>> and would make sense.
>>>
>>> > I think supporting this gives lots of potential problems without
>>> much in the way of gain. Even the "correct" form Coalesce('-title',
>>> '-description') just looks odd to me. The DESC modifier applies to the
>>> expression as a whole, not to any one field within it
>>>
>>> Totally agree. Ordering applies to the expression, not any one node
>>> within that expression. So we need to figure out how best to support:
>>>
>>> .order_by( F('field_a')+F('field_b') )
>>>
>>>
>>> You can't apply ordering to field_a or field_b, because the returned
>>> Expression is actually `+ (field_a, field_b)`. Considering that, a wrapping
>>> `Desc(expression)` probably makes a lot of sense, but has the following
>>> problems:
>>>
>>> 1. The syntax is somewhat ugly, but fits with the theme of composable
>>> expressions.
>>> 2. If `Desc()` is just another expression, you'd need to prevent it from
>>> being used in an `inner` context. It can only be a wrapper. I think
>>> documenting this limitation is fine - but if there was a way to throw a
>>> useful error (rather than the database error that would surely follow),
>>> it'd be nice.
>>> 3. order_by(F('-field_a')) wouldn't be possible - there's no way for the
>>> `F()` to push the `Desc()` to the top of the tree. Since this isn't already
>>> supported anyway, I don't think it's that big of a deal.
>>> 4. order_by('-field_a') should produce: Desc(F('field_a')), but I think
>>> that'd be very easy. Expressions should be the only supported API for
>>> order_by, except a string referencing a field (like it is now) that is
>>> internally converted to an Expression. I do the same thing with Aggregates
>>> right now.
>>>
>>> > An alternative is to use ~ instead of - meaning inverse instead of
>>> negative. This might be more appropriate (but then is ~LowerCase not
>>> UpperCase?).
>>>
>>> I don't think anyone would ever expect ~LowerCase to mean UpperCase,
>>> but it is still somewhat confusing syntax. ~ is mostly used in Q() objects
>>> to mean NOT, and could be very confusing. We also run into similar issues
>>> with the `order_by( F()+F() )` use case above - applying a unary operator
>>> to either of those objects won't actually result in any ordering being
>>> applied.
>>>
>>> Josh
>>>
>>> On Monday, 9 June 2014 02:07:42 UTC+10, Tim Martin wrote:
>>>>
>>>>
>>>> On Sunday, 8 June 2014 13:24:01 UTC+1, Josh Smeaton wrote:
>>>>>
>>>>> I've thought about this previously, and what I had in mind was:
>>>>>
>>>>> - Expressions should support an ordering parameter of some kind in
>>>>> the constructor, and save it to an ordering property. Default to ASC
>>>>> - If we go with Marc's suggestion, the __neg__ can set the above
>>>>> property. This would look very consistent with the existing ordering api.
>>>>> - F() objects should parse their field argument, and internally set
>>>>> their ordering attribute appropriately -> F('-title') should strip the
>>>>> `-`
>>>>> and set a DESC ordering.
>>>>> - Since expressions can be nested, you can't return ASC/DESC from the
>>>>> as_sql() method, otherwise you'd end up with weirdness like:
>>>>> Coalesce(field_a
>>>>> ASC, field_b ASC) instead of Coalesce(field_a, field_b) ASC.
>>>>> Therefore, the order_by machinery should call a `get_ordering()` (or
>>>>> something named similarly) and generate the ASC/DESC token accordingly.
>>>>>
>>>>
>>>> That sounds like a workable approach. If I'm understanding right, this
>>>> means putting the ordering flag on the ExpressionNode class so that all
>>>> nodes will have this whether or not they are to be used in an ordering
>>>> context?
>>>>
>>>> My only reservation about this when combined with __neg__ is that it
>>>> means that certain mathematical expressions will behave oddly. For
>>>> example,
>>>> you might want to do something like
>>>>
>>>> Accounts.objects.filter(balance__lt=-F('overdraft_limit'))
>>>>
>>>> but this will end up flipping the ordering rather than doing a
>>>> mathematical negative. Is this dealt with by putting logic into the
>>>> SQLEvaluator so that it knows whether it's evaluating in an ordering
>>>> context or a SELECT or WHERE clause context and does the right thing?
>>>>
>>>>
>>>>> Usage:
>>>>>
>>>>> Article.objects.order_by(LowerCase('title', ordering='-'))
>>>>>
>>>>> Article.objects.order_by(-LowerCase('title')) # I prefer this form
>>>>>
>>>>> Article.objects.order_by('-title') and
>>>>> Article.objects.order_by(F('-title')) # should be equivalent (a
>>>>> string argument should be converted to an F())
>>>>>
>>>>>
>>>>> I'm unsure whether or not expressions should inspect their
>>>>> "children" for an ordering if one isn't supplied in the outer-level:
>>>>>
>>>>> Article.objects.order_by(LowerCase('-title'))
>>>>>
>>>>>
>>>>> Should the Lowercase try to resolve the ordering of `title` and use
>>>>> it as it's own? I don't think so, because it could lead to weird
>>>>> conflicts
>>>>> where multiple arguments define different ordering:
>>>>>
>>>>> Article.objects.order_by(Coalesce('-title', '+description')) # is
>>>>> ASC or DESC used? Possibly confusing
>>>>>
>>>>>
>>>>> Thoughts?
>>>>>
>>>>
>>>>
>>>> I think supporting this gives lots of potential problems without much
>>>> in the way of gain. Even the "correct" form Coalesce('-title',
>>>> '-description') just looks odd to me. The DESC modifier applies to the
>>>> expression as a whole, not to any one field within it.
>>>>
>>>> Tim
>>>>
>>> --
>>> You received this message because you are subscribed to the Google
>>> Groups "Django developers" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/django-developers.
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/django-developers/6365dba1-2dfc-469d-988b-3cb800cc0f76%40googlegroups.com
>>>
>>> <https://groups.google.com/d/msgid/django-developers/6365dba1-2dfc-469d-988b-3cb800cc0f76%40googlegroups.com?utm_medium=email&utm_source=footer>.
>>>
>>>
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Django developers" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To post to this group, send email to [email protected].
>> Visit this group at http://groups.google.com/group/django-developers.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/django-developers/CAMwjO1E36SeDW4R_jQOcw%3DkGV%2BwG5uaE%3D1Vif5DyDgwJY3ikcA%40mail.gmail.com
>>
>> <https://groups.google.com/d/msgid/django-developers/CAMwjO1E36SeDW4R_jQOcw%3DkGV%2BwG5uaE%3D1Vif5DyDgwJY3ikcA%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
> You received this message because you are subscribed to the Google Groups
> "Django developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected] <javascript:>.
> To post to this group, send email to [email protected]
> <javascript:>.
> Visit this group at http://groups.google.com/group/django-developers.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/4e8e8101-87d2-4d4a-8c1c-79f885fe5291%40googlegroups.com
>
> <https://groups.google.com/d/msgid/django-developers/4e8e8101-87d2-4d4a-8c1c-79f885fe5291%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>
>
>
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/18d0d818-5ae2-4087-9726-9b9cfa2ce3b1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.