#26608: Window functions
-------------------------------+--------------------
     Reporter:  daggaz         |      Owner:  nobody
         Type:  New feature    |     Status:  new
    Component:  Uncategorized  |    Version:  1.9
     Severity:  Normal         |   Keywords:
 Triage Stage:  Unreviewed     |  Has patch:  0
Easy pickings:  0              |      UI/UX:  0
-------------------------------+--------------------
 Add the ability to use SQL window functions (`SELECT ... OVER (...) FROM
 ...`) to the Django ORM.

 I had a go at writing a window function Expression myself. I was mostly
 guessing at how the API is supposed to work, so improvements would be
 welcome. This code came about in response to #26602.

 {{{#!python
 class Window(Expression):
     template = '%(expression)s OVER (%(window)s)'

     def __init__(self, expression, partition_by=None, order_by=None,
 output_field=None):
         self.order_by = order_by
         if isinstance(order_by, six.string_types):
             if order_by.startswith('-'):
                 self.order_by = OrderBy(F(self.order_by[1:]),
 descending=True)
             else:
                 self.order_by = OrderBy(F(self.order_by))

         self.partition_by = partition_by
         if self.partition_by:
             self.partition_by = self._parse_expressions(partition_by)[0]

         super(Window, self).__init__(output_field=output_field)
         self.source_expression = self._parse_expressions(expression)[0]
         if not getattr(self.source_expression, 'contains_aggregate',
 False):
             raise FieldError("Window function expressions must be
 aggregate functions")

     def _resolve_output_field(self):
         if self._output_field is None:
             self._output_field = self.source_expression.output_field

     def resolve_expression(self, query=None, allow_joins=True, reuse=None,
 summarize=False, for_save=False):
         c = self.copy()
         c.source_expression =
 c.source_expression.resolve_expression(query, allow_joins, reuse,
 summarize, for_save)
         if c.partition_by:
             c.partition_by = c.partition_by.resolve_expression(query,
 allow_joins, reuse, summarize, for_save)
         if c.order_by:
             c.order_by = c.order_by.resolve_expression(query, allow_joins,
 reuse, summarize, for_save)
         c.is_summary = summarize
         c.for_save = for_save
         return c

     def as_sql(self, compiler, connection, function=None, template=None):
         connection.ops.check_expression_support(self)
         expr_sql, params = compiler.compile(self.source_expression)

         window_sql = []
         if self.partition_by:
             window_sql.append('PARTITION BY ')
             order_sql, order_params = compiler.compile(self.partition_by)
             window_sql.append(order_sql)
             params.extend(order_params)
         if self.order_by:
             window_sql.append(' ORDER BY ')
             order_sql, order_params = compiler.compile(self.order_by)
             window_sql.append(order_sql)
             params.extend(order_params)
         template = template or self.template
         return template % {'expression': expr_sql, 'window':
 "".join(window_sql)}, params

     def copy(self):
         copy = super(Window, self).copy()
         copy.source_expression = self.source_expression.copy()
         copy.partition_by = self.partition_by if self.partition_by else
 None
         copy.order_by = self.order_by.copy() if self.order_by else None
         return copy

     def get_group_by_cols(self):
         return []
 }}}

 Allowing you to write:

 {{{#!python
 class A(models.Model):
     account = models.IntegerField()
     amount = models.DecimalField(max_digits=10, decimal_places=2)
     created = models.DateTimeField()

 A.objects.annotate(
     balance=Window(Sum('amount'), partition_by='account',
 order_by='created'),
 )
 }}}

--
Ticket URL: <https://code.djangoproject.com/ticket/26608>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" 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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/049.2bf922cecaed2e329c70373f91db338b%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to