From: Mike Bayer Date: Fri, 26 Aug 2016 16:56:01 +0000 (-0400) Subject: Raise when two validators conflict X-Git-Tag: rel_1_1_0~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=117997cd5b849a236208cf9ea16f0f09c6962804;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Raise when two validators conflict Two @validates decorators that make use of the same name is not supported. Raise an exception. Change-Id: Ia3e89ffdc9ef345a0de258e2ac0ac5e0bd421c61 Fixes: #3776 --- diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index e08976f152..ba967f9762 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -21,6 +21,17 @@ .. 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 diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index b76a6f7271..e8aa085417 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 417554f468..e59bb580a0 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)