summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Fedoseev <fedoseev.sergey@gmail.com>2015-10-31 15:01:08 +0500
committerClaude Paroz <claude@2xlibre.net>2015-11-02 20:09:21 +0100
commit0a26121797b5e0aa07ada07fe1577a79d3853c40 (patch)
tree2c19e8bb72ab937225804c9aa0a0f6b4693c311e
parent03c6ad7ad44032206a8780f74c693d16ca1f1a37 (diff)
Refs #25629 -- Added `arity` class attribute to `Func` expressions
-rw-r--r--django/db/models/expressions.py10
-rw-r--r--django/db/models/functions.py6
-rw-r--r--django/db/models/lookups.py5
-rw-r--r--docs/ref/models/expressions.txt9
-rw-r--r--docs/releases/1.10.txt4
-rw-r--r--tests/db_functions/tests.py3
6 files changed, 27 insertions, 10 deletions
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index d9540055b9..1497681d57 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -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)
diff --git a/django/db/models/functions.py b/django/db/models/functions.py
index ffa3e36084..4cbcc9c899 100644
--- a/django/db/models/functions.py
+++ b/django/db/models/functions.py
@@ -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)
diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py
index b50711ada4..067722b6bb 100644
--- a/django/db/models/lookups.py
+++ b/django/db/models/lookups.py
@@ -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):
diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt
index 87c3108509..98ba323171 100644
--- a/docs/ref/models/expressions.txt
+++ b/docs/ref/models/expressions.txt
@@ -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``
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 8846dce242..20afbd93b8 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -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
^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/db_functions/tests.py b/tests/db_functions/tests.py
index c18a4b25af..491ce6cefe 100644
--- a/tests/db_functions/tests.py
+++ b/tests/db_functions/tests.py
@@ -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')