<p dir="auto"><strong>Migrated issue, originally created by Dariusz Górecki (<a href="https://github.com/canni">@canni</a>)</strong></p>
<p dir="auto">Consider following example of joined-table inheritance:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class Parent(DeclarativeBase):
    # ...
    parent_column = Column(String(255))

class Child(Parent):
    # ...
    child_column = synonym('parent_column')

    @validates('child_column')  # @validates('parent_column') also does not work
    def validator(self, name, value):
        # not called at all
        raise ValueError()"><pre class="notranslate"><code class="notranslate">class Parent(DeclarativeBase):
    # ...
    parent_column = Column(String(255))

class Child(Parent):
    # ...
    child_column = synonym('parent_column')

    @validates('child_column')  # @validates('parent_column') also does not work
    def validator(self, name, value):
        # not called at all
        raise ValueError()
</code></pre></div>
<p dir="auto">The <code class="notranslate">validator</code> method is never called</p>
<p dir="auto"><strong>Migrated issue, originally created by Michael Bayer (<a href="https://github.com/zzzeek">@zzzeek</a>)</strong></p>
<p dir="auto">#!python<br>
from sqlalchemy import *<br>
from sqlalchemy.orm import *<br>
from sqlalchemy.ext.declarative import declarative_base</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="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 &quot;yup A &quot; + value


class B(A):
    @validates('data')
    def validate(self, key, value):
        return &quot;yup B &quot; + value

for obj in (A(), B()):
    obj.data = &quot;value&quot;
    print(obj.data)"><pre class="notranslate"><code class="notranslate">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)
</code></pre></div>
<p dir="auto">so here's a patch that makes the second one print "yup A yup B value", e.g. runs both validators:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="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](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)"><pre class="notranslate"><code class="notranslate">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](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)
</code></pre></div>
<p dir="auto">but is that what we want?  or should B's validator replace A's?</p>
<p dir="auto">if the latter then we just do this:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="            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"><pre class="notranslate"><code class="notranslate">            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
</code></pre></div>
<hr>
<p dir="auto">Attachments: <a href="../wiki/imported_issue_attachments/2943/2943.patch">2943.patch</a></p>