summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorKai Feldhoff <kaifeldhoff@yahoo.de>2016-03-16 13:22:07 -0400
committerTim Graham <timograham@gmail.com>2016-03-21 20:20:29 -0400
commit5ca08f7cab4d65f9bd49bd8a1817dd4b6591cab1 (patch)
treec17aab548a1a97b195b53f80f696d354a2d627c8 /docs
parentbaa8b0ec399bd9b0ff3308666b49c6ab734aa416 (diff)
Refs #25759 -- Documented customizing expressions' SQL on other databases.
Diffstat (limited to 'docs')
-rw-r--r--docs/ref/models/expressions.txt43
-rw-r--r--docs/ref/models/lookups.txt5
2 files changed, 41 insertions, 7 deletions
diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt
index c5a433c94c..8dc30a91c4 100644
--- a/docs/ref/models/expressions.txt
+++ b/docs/ref/models/expressions.txt
@@ -261,6 +261,28 @@ The ``Func`` API is as follows:
different number of expressions, ``TypeError`` will be raised. Defaults
to ``None``.
+ .. method:: as_sql(compiler, connection, function=None, template=None)
+
+ Generates the SQL for the database function.
+
+ The ``as_vendor()`` methods should use the ``function`` and
+ ``template`` parameters to customize the SQL as needed. For example:
+
+ .. snippet::
+ :filename: django/db/models/functions.py
+
+ class ConcatPair(Func):
+ ...
+ function = 'CONCAT'
+ ...
+
+ def as_mysql(self, compiler, connection):
+ return super(ConcatPair, self).as_sql(
+ compiler, connection,
+ function='CONCAT_WS',
+ template="%(function)s('', %(expressions)s)",
+ )
+
The ``*expressions`` argument is a list of positional expressions that the
function will be applied to. The expressions will be converted to strings,
joined together with ``arg_joiner``, and then interpolated into the ``template``
@@ -560,7 +582,7 @@ an ``__init__()`` method to set some attributes::
class Coalesce(Expression):
template = 'COALESCE( %(expressions)s )'
- def __init__(self, expressions, output_field, **extra):
+ def __init__(self, expressions, output_field):
super(Coalesce, self).__init__(output_field=output_field)
if len(expressions) < 2:
raise ValueError('expressions must have at least 2 elements')
@@ -568,7 +590,6 @@ an ``__init__()`` method to set some attributes::
if not hasattr(expression, 'resolve_expression'):
raise TypeError('%r is not an Expression' % expression)
self.expressions = expressions
- self.extra = extra
We do some basic validation on the parameters, including requiring at least
2 columns or values, and ensuring they are expressions. We are requiring
@@ -588,22 +609,30 @@ expressions::
Next, we write the method responsible for generating the SQL::
- def as_sql(self, compiler, connection):
+ def as_sql(self, compiler, connection, template=None):
sql_expressions, sql_params = [], []
for expression in self.expressions:
sql, params = compiler.compile(expression)
sql_expressions.append(sql)
sql_params.extend(params)
- self.extra['expressions'] = ','.join(sql_expressions)
- return self.template % self.extra, sql_params
+ template = template or self.template
+ data = {'expressions': ','.join(sql_expressions)}
+ return template % data, params
def as_oracle(self, compiler, connection):
"""
Example of vendor specific handling (Oracle in this case).
Let's make the function name lowercase.
"""
- self.template = 'coalesce( %(expressions)s )'
- return self.as_sql(compiler, connection)
+ return self.as_sql(compiler, connection, template='coalesce( %(expressions)s )')
+
+``as_sql()`` methods can support custom keyword arguments, allowing
+``as_vendorname()`` methods to override data used to generate the SQL string.
+Using ``as_sql()`` keyword arguments for customization is preferable to
+mutating ``self`` within ``as_vendorname()`` methods as the latter can lead to
+errors when running on different database backends. If your class relies on
+class attributes to define data, consider allowing overrides in your
+``as_sql()`` method.
We generate the SQL for each of the ``expressions`` by using the
``compiler.compile()`` method, and join the result together with commas.
diff --git a/docs/ref/models/lookups.txt b/docs/ref/models/lookups.txt
index fa020f8e30..49083c1f61 100644
--- a/docs/ref/models/lookups.txt
+++ b/docs/ref/models/lookups.txt
@@ -94,6 +94,11 @@ following methods:
``compiler.compile(expression)`` should be used. The ``compiler.compile()``
method will take care of calling vendor-specific methods of the expression.
+ Custom keyword arguments may be defined on this method if it's likely that
+ ``as_vendorname()`` methods or subclasses will need to supply data to
+ override the generation of the SQL string. See :meth:`Func.as_sql` for
+ example usage.
+
.. method:: as_vendorname(self, compiler, connection)
Works like ``as_sql()`` method. When an expression is compiled by