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 function = None
template = '%(function)s(%(expressions)s)' template = '%(function)s(%(expressions)s)'
arg_joiner = ', ' arg_joiner = ', '
arity = None # The number of arguments the function accepts.
def __init__(self, *expressions, **extra): 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) output_field = extra.pop('output_field', None)
super(Func, self).__init__(output_field=output_field) super(Func, self).__init__(output_field=output_field)
self.source_expressions = self._parse_expressions(*expressions) self.source_expressions = self._parse_expressions(*expressions)

View File

@ -145,9 +145,6 @@ class Lower(Transform):
function = 'LOWER' function = 'LOWER'
lookup_name = 'lower' lookup_name = 'lower'
def __init__(self, expression, **extra):
super(Lower, self).__init__(expression, **extra)
class Now(Func): class Now(Func):
template = 'CURRENT_TIMESTAMP' template = 'CURRENT_TIMESTAMP'
@ -197,6 +194,3 @@ class Substr(Func):
class Upper(Transform): class Upper(Transform):
function = 'UPPER' function = 'UPPER'
lookup_name = '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. first examine self and then check output_field.
""" """
bilateral = False bilateral = False
arity = 1
def __init__(self, expression, **extra):
# Restrict Transform to allow only a single expression.
super(Transform, self).__init__(expression, **extra)
@property @property
def lhs(self): 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 A class attribute that denotes the character used to join the list of
``expressions`` together. Defaults to ``', '``. ``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 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``

View File

@ -175,6 +175,10 @@ Models
accessible as a descriptor on the proxied model class and may be referenced in accessible as a descriptor on the proxied model class and may be referenced in
queryset filtering. 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 Requests and Responses
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^

View File

@ -389,6 +389,9 @@ class FunctionTests(TestCase):
lambda a: (a.lower_name, a.name) 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): def test_upper(self):
Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda') Author.objects.create(name='Rhonda')