Refs #25629 -- Added `arity` class attribute to `Func` expressions

This commit is contained in:
Sergey Fedoseev 2015-10-31 15:01:08 +05:00 committed by Claude Paroz
parent 03c6ad7ad4
commit 0a26121797
6 changed files with 27 additions and 10 deletions

View File

@ -482,8 +482,18 @@ class Func(Expression):
function = None
template = '%(function)s(%(expressions)s)'
arg_joiner = ', '
arity = None # The number of arguments the function accepts.
def __init__(self, *expressions, **extra):
if self.arity is not None and len(expressions) != self.arity:
raise TypeError(
"'%s' takes exactly %s %s (%s given)" % (
self.__class__.__name__,
self.arity,
"argument" if self.arity == 1 else "arguments",
len(expressions),
)
)
output_field = extra.pop('output_field', None)
super(Func, self).__init__(output_field=output_field)
self.source_expressions = self._parse_expressions(*expressions)

View File

@ -145,9 +145,6 @@ class Lower(Transform):
function = 'LOWER'
lookup_name = 'lower'
def __init__(self, expression, **extra):
super(Lower, self).__init__(expression, **extra)
class Now(Func):
template = 'CURRENT_TIMESTAMP'
@ -197,6 +194,3 @@ class Substr(Func):
class Upper(Transform):
function = 'UPPER'
lookup_name = 'upper'
def __init__(self, expression, **extra):
super(Upper, self).__init__(expression, **extra)

View File

@ -123,10 +123,7 @@ class Transform(RegisterLookupMixin, Func):
first examine self and then check output_field.
"""
bilateral = False
def __init__(self, expression, **extra):
# Restrict Transform to allow only a single expression.
super(Transform, self).__init__(expression, **extra)
arity = 1
@property
def lhs(self):

View File

@ -252,6 +252,15 @@ The ``Func`` API is as follows:
A class attribute that denotes the character used to join the list of
``expressions`` together. Defaults to ``', '``.
.. attribute:: arity
.. versionadded:: 1.10
A class attribute that denotes the number of arguments the function
accepts. If this attribute is set and the function is called with a
different number of expressions, ``TypeError`` will be raised. Defaults
to ``None``.
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``

View File

@ -175,6 +175,10 @@ Models
accessible as a descriptor on the proxied model class and may be referenced in
queryset filtering.
* The :attr:`~django.db.models.Func.arity` class attribute is added to
:class:`~django.db.models.Func`. This attribute can be used to set the number
of arguments the function accepts.
Requests and Responses
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -389,6 +389,9 @@ class FunctionTests(TestCase):
lambda a: (a.lower_name, a.name)
)
with self.assertRaisesMessage(TypeError, "'Lower' takes exactly 1 argument (2 given)"):
Author.objects.update(name=Lower('name', 'name'))
def test_upper(self):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda')