.. changelog::
: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
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)}
)
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):
]
)
+ 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)