summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/changelog_11.rst11
-rw-r--r--lib/sqlalchemy/orm/mapper.py5
-rw-r--r--test/orm/test_validators.py39
3 files changed, 54 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index e08976f15..ba967f976 100644
--- a/doc/build/changelog/changelog_11.rst
+++ b/doc/build/changelog/changelog_11.rst
@@ -22,6 +22,17 @@
:version: 1.1.0
.. change::
+ :tags: bug, orm
+ :tickets: 3776
+
+ An exception is raised when two ``@validates`` decorators on a mapping
+ make use of the same name. Only one validator of a certain name
+ at a time is supported, there's no mechanism to chain these together,
+ as the order of the validators at the level of function decorator
+ can't be made deterministic.
+
+
+ .. change::
:tags: bug, mysql
:tickets: 3766
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index b76a6f727..e8aa08541 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1204,6 +1204,11 @@ class Mapper(InspectionAttr):
elif hasattr(method, '__sa_validators__'):
validation_opts = method.__sa_validation_opts__
for name in method.__sa_validators__:
+ if name in self.validators:
+ raise sa_exc.InvalidRequestError(
+ "A validation function for mapped "
+ "attribute %r on mapper %s already exists." %
+ (name, self))
self.validators = self.validators.union(
{name: (method, validation_opts)}
)
diff --git a/test/orm/test_validators.py b/test/orm/test_validators.py
index 417554f46..e59bb580a 100644
--- a/test/orm/test_validators.py
+++ b/test/orm/test_validators.py
@@ -1,7 +1,9 @@
from test.orm import _fixtures
-from sqlalchemy.testing import fixtures, assert_raises, eq_, ne_
+from sqlalchemy.testing import fixtures, assert_raises, eq_, ne_, \
+ assert_raises_message
from sqlalchemy.orm import mapper, Session, validates, relationship
from sqlalchemy.testing.mock import Mock, call
+from sqlalchemy import exc
class ValidatorTest(_fixtures.FixtureTest):
@@ -145,6 +147,41 @@ class ValidatorTest(_fixtures.FixtureTest):
]
)
+ def test_validator_multi_warning(self):
+ users = self.tables.users
+
+ class Foo(object):
+ @validates("name")
+ def validate_one(self, key, value):
+ pass
+
+ @validates("name")
+ def validate_two(self, key, value):
+ pass
+
+ assert_raises_message(
+ exc.InvalidRequestError,
+ "A validation function for mapped attribute "
+ "'name' on mapper Mapper|Foo|users already exists",
+ mapper, Foo, users
+ )
+
+ class Bar(object):
+ @validates("id")
+ def validate_three(self, key, value):
+ return value + 10
+
+ @validates("id", "name")
+ def validate_four(self, key, value):
+ return value + "foo"
+
+ assert_raises_message(
+ exc.InvalidRequestError,
+ "A validation function for mapped attribute "
+ "'name' on mapper Mapper|Bar|users already exists",
+ mapper, Bar, users
+ )
+
def test_validator_wo_backrefs_wo_removes(self):
self._test_validator_backrefs(False, False)