Kaydet (Commit) 5ca08f7c authored tarafından Kai Feldhoff's avatar Kai Feldhoff Kaydeden (comit) Tim Graham

Refs #25759 -- Documented customizing expressions' SQL on other databases.

üst baa8b0ec
...@@ -261,6 +261,28 @@ The ``Func`` API is as follows: ...@@ -261,6 +261,28 @@ The ``Func`` API is as follows:
different number of expressions, ``TypeError`` will be raised. Defaults different number of expressions, ``TypeError`` will be raised. Defaults
to ``None``. 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 The ``*expressions`` argument is a list of positional expressions that the
function will be applied to. The expressions will be converted to strings, function will be applied to. The expressions will be converted to strings,
joined together with ``arg_joiner``, and then interpolated into the ``template`` joined together with ``arg_joiner``, and then interpolated into the ``template``
...@@ -560,7 +582,7 @@ an ``__init__()`` method to set some attributes:: ...@@ -560,7 +582,7 @@ an ``__init__()`` method to set some attributes::
class Coalesce(Expression): class Coalesce(Expression):
template = 'COALESCE( %(expressions)s )' 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) super(Coalesce, self).__init__(output_field=output_field)
if len(expressions) < 2: if len(expressions) < 2:
raise ValueError('expressions must have at least 2 elements') raise ValueError('expressions must have at least 2 elements')
...@@ -568,7 +590,6 @@ an ``__init__()`` method to set some attributes:: ...@@ -568,7 +590,6 @@ an ``__init__()`` method to set some attributes::
if not hasattr(expression, 'resolve_expression'): if not hasattr(expression, 'resolve_expression'):
raise TypeError('%r is not an Expression' % expression) raise TypeError('%r is not an Expression' % expression)
self.expressions = expressions self.expressions = expressions
self.extra = extra
We do some basic validation on the parameters, including requiring at least We do some basic validation on the parameters, including requiring at least
2 columns or values, and ensuring they are expressions. We are requiring 2 columns or values, and ensuring they are expressions. We are requiring
...@@ -588,22 +609,30 @@ expressions:: ...@@ -588,22 +609,30 @@ expressions::
Next, we write the method responsible for generating the SQL:: 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 = [], [] sql_expressions, sql_params = [], []
for expression in self.expressions: for expression in self.expressions:
sql, params = compiler.compile(expression) sql, params = compiler.compile(expression)
sql_expressions.append(sql) sql_expressions.append(sql)
sql_params.extend(params) sql_params.extend(params)
self.extra['expressions'] = ','.join(sql_expressions) template = template or self.template
return self.template % self.extra, sql_params data = {'expressions': ','.join(sql_expressions)}
return template % data, params
def as_oracle(self, compiler, connection): def as_oracle(self, compiler, connection):
""" """
Example of vendor specific handling (Oracle in this case). Example of vendor specific handling (Oracle in this case).
Let's make the function name lowercase. Let's make the function name lowercase.
""" """
self.template = 'coalesce( %(expressions)s )' return self.as_sql(compiler, connection, template='coalesce( %(expressions)s )')
return self.as_sql(compiler, connection)
``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 We generate the SQL for each of the ``expressions`` by using the
``compiler.compile()`` method, and join the result together with commas. ``compiler.compile()`` method, and join the result together with commas.
......
...@@ -94,6 +94,11 @@ following methods: ...@@ -94,6 +94,11 @@ following methods:
``compiler.compile(expression)`` should be used. The ``compiler.compile()`` ``compiler.compile(expression)`` should be used. The ``compiler.compile()``
method will take care of calling vendor-specific methods of the expression. 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) .. method:: as_vendorname(self, compiler, connection)
Works like ``as_sql()`` method. When an expression is compiled by Works like ``as_sql()`` method. When an expression is compiled by
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment