Hi all,
I was looking at implementing the behaviour proposed in #13006
(https://code.djangoproject.com/ticket/13006). In short, the idea is to
allow decorating fields in order_by() with a LowerCase() modifier to apply
LOWER() in SQL before ordering:
Articles.objects.all().order_by(LowerCase('title'))
to yield
...
ORDER BY LOWER("articles"."title")
...
Obviously it's sensible if we can generalise this to apply a wide range of
SQL functions with the same infrastructure.
It's easy enough to make this work, but I'm having trouble with
generalising the solution. The problem is that the argument to order_by()
can be a compound expression like "-author__name" and we have to parse it
out. Only the SQLCompiler object (I think) has the necessary information to
correctly figure out the joins and turn this into valid SQL.
I can see a couple of different approaches:
1) Have these decorator classes be very simple containers and keep all the
logic in the SQLCompiler. The compiler object will detect a
FunctionCallDecorator, query it for the ordering string and combine them,
something like:
if isinstance(field, FunctionCallDecorator):
table, cols, order = self.find_ordering_name(field.get_field(), ...)
function = field.get_function()
# other stuff....
order_by = "%s(%s.%s)" % (function, qn(table), qn2(col))
I'm not that happy with this solution, since it binds the implementation of
FunctionCallDecorator very closely to the workings of SQLCompiler and to
ORDER BY in particular.
2) Make the argument to order_by into a tree structure that can be
processed by the SQLCompiler using the visitor pattern. Leaf nodes
(strings) get mapped into expressions by the SQLCompiler as normal, while
internal nodes get to define their own logic for combining the output of
their child nodes into an expression. The ASC / DESC direction is a bit
awkward here, since it's meaningless to have two function arguments that
order in different directions, for example.
This is more similar to how we do WHERE nodes, if I'm understanding the
code correctly. This seems to better support multi-argument functions (e.g.
COALESCE) which would be a horrible kludge in the first method, and to
support multiple function application (e.g. LOWER(LTRIM(...))). However, it
feels a bit over-engineered.
There are obviously lots of other variations here, but I figured I'd ask
before I went too much further with the design process. Does anyone have
any feelings about how best to approach this?
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/141c1486-ca35-497d-82f7-13ad83900b9d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.