[Sqlalchemy-tickets] [sqlalchemy] #2943: @validates on inherited subclasses
Brought to you by:
zzzeek
|
From: sqlalchemy <mi...@zz...> - 2014-02-07 17:50:09
|
#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
Keywords: | Progress State: in queue
--------------------+------------------------------------
{{{
#!python
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
data = Column(String)
@validates('data')
def validate(self, key, value):
return "yup A " + value
class B(A):
@validates('data')
def validate(self, key, value):
return "yup B " + value
for obj in (A(), B()):
obj.data = "value"
print(obj.data)
}}}
so here's a patch that makes the second one print "yup A yup B value",
e.g. runs both validators:
{{{
#!diff
diff --git a/lib/sqlalchemy/orm/strategies.py
b/lib/sqlalchemy/orm/strategies.py
index 2c18e81..fb8e0aa 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -38,26 +38,21 @@ 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)
+ pre_validate_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)
- )
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 +80,15 @@ 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]
+ orm_util._validator_events(desc, prop.key, fn,
**opts)
+
+ for hook in post_validate_hooks:
hook(desc, prop)
@properties.ColumnProperty.strategy_for(instrument=False, deferred=False)
}}}
but is that what we want? or should B's validator replace A's?
if the latter then we just do this:
{{{
#!python
for super_m in m.iterate_to_root():
if prop.key in super_m.validators:
fn, opts = super_m.validators[prop.key]
orm_util._validator_events(desc, prop.key, fn, **opts)
break # just the one
}}}
--
Ticket URL: <http://www.sqlalchemy.org/trac/ticket/2943>
sqlalchemy <http://www.sqlalchemy.org/>
The Database Toolkit for Python
|