Re: [Sqlalchemy-tickets] [sqlalchemy] #2943: @validates on inherited subclasses
Brought to you by:
zzzeek
|
From: sqlalchemy <mi...@zz...> - 2014-02-07 18:08:07
|
#2943: @validates on inherited subclasses
---------------------------+-------------------------------
Reporter: zzzeek | Owner: zzzeek
Type: defect | Status: new
Priority: high | Milestone: 0.9.3
Component: orm | Severity: major - 1-3 hours
Resolution: | Keywords:
Progress State: in queue |
---------------------------+-------------------------------
Comment (by zzzeek):
here's that....
im a little concerned about setup performance here.
{{{
#!diff
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 26f105b..7928310 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2603,6 +2603,7 @@ def validates(*names, **kw):
argument "is_remove" which will be a boolean.
.. versionadded:: 0.7.7
+
:param include_backrefs: defaults to ``True``; if ``False``, the
validation function will not emit if the originator is an attribute
event related via a backref. This can be used for bi-directional
@@ -2611,6 +2612,15 @@ def validates(*names, **kw):
.. versionadded:: 0.9.0
+ :param override: if ``True``, this validation function can be
+ specified on a subclass which inherits the mapped attribute,
+ where it will override any existing validators on the superclass.
+ If not present, a ``@validates`` decorator on a subclass raises an
+ error, as it currently takes no effect.
+
+ .. versionadded:: 0.9.3
+
+
.. seealso::
:ref:`simple_validators` - usage examples for :func:`.validates`
@@ -2618,12 +2628,14 @@ def validates(*names, **kw):
"""
include_removes = kw.pop('include_removes', False)
include_backrefs = kw.pop('include_backrefs', True)
+ override = kw.pop('override', False)
def wrap(fn):
fn.__sa_validators__ = names
fn.__sa_validation_opts__ = {
"include_removes": include_removes,
- "include_backrefs": include_backrefs
+ "include_backrefs": include_backrefs,
+ "override": override
}
return fn
return wrap
diff --git a/lib/sqlalchemy/orm/strategies.py
b/lib/sqlalchemy/orm/strategies.py
index 2c18e81..95b449d 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -38,26 +38,20 @@ def _register_attribute(strategy, mapper, useobject,
attribute_ext = list(util.to_list(prop.extension, default=[]))
- listen_hooks = []
+ pre_validate_hooks = []
+ post_validate_hooks = []
if useobject and prop.single_parent:
- listen_hooks.append(single_parent_validator)
-
- if prop.key in prop.parent.validators:
- fn, opts = prop.parent.validators[prop.key]
- listen_hooks.append(
- lambda desc, prop: orm_util._validator_events(desc,
- prop.key, fn, **opts)
- )
+ pre_validate_hooks.append(single_parent_validator)
if useobject:
- listen_hooks.append(unitofwork.track_cascade_events)
+ post_validate_hooks.append(unitofwork.track_cascade_events)
# need to assemble backref listeners
# after the singleparentvalidator, mapper validator
backref = kw.pop('backref', None)
if backref:
- listen_hooks.append(
+ post_validate_hooks.append(
lambda desc, prop: attributes.backref_listeners(desc,
backref,
uselist)
@@ -85,7 +79,21 @@ def _register_attribute(strategy, mapper, useobject,
**kw
)
- for hook in listen_hooks:
+ for hook in pre_validate_hooks:
+ hook(desc, prop)
+
+ for super_m in m.iterate_to_root():
+ if prop.key in super_m.validators:
+ fn, opts = super_m.validators[prop.key]
+ if not opts["override"] and super_m is not mapper:
+ raise sa_exc.InvalidRequestError(
+ "validator on inheriting class %s.%s
won't "
+ "run by default; please specify
override=True" %
+ (super_m.class_.__name__, prop.key))
+ orm_util._validator_events(desc, prop.key, fn,
**opts)
+ break
+
+ for hook in post_validate_hooks:
hook(desc, prop)
@properties.ColumnProperty.strategy_for(instrument=False, deferred=False)
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index dd85f2e..3a05b84 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -70,7 +70,7 @@ class CascadeOptions(frozenset):
)
-def _validator_events(desc, key, validator, include_removes,
include_backrefs):
+def _validator_events(desc, key, validator, include_removes,
include_backrefs, override):
"""Runs a validation method on an attribute value to be set or
appended."""
if not include_backrefs:
}}}
--
Ticket URL: <http://www.sqlalchemy.org/trac/ticket/2943#comment:2>
sqlalchemy <http://www.sqlalchemy.org/>
The Database Toolkit for Python
|