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] <mailto:[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]
    <mailto:[email protected]>.
    To post to this group, send email to
    [email protected]
    <mailto:[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] <mailto:[email protected]>. To post to this group, send email to [email protected] <mailto:[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].
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/53958730.80401%40thl.fi.
For more options, visit https://groups.google.com/d/optout.

Reply via email to