diff --git a/facho/model/__init__.py b/facho/model/__init__.py
index 51a9345..f64bd55 100644
--- a/facho/model/__init__.py
+++ b/facho/model/__init__.py
@@ -1,4 +1,5 @@
from .fields import Field
+from collections import defaultdict
class ModelMeta(type):
def __new__(cls, name, bases, ns):
@@ -20,6 +21,19 @@ class ModelBase(object, metaclass=ModelMeta):
obj._fields = {}
obj._text = ""
obj._namespace_prefix = None
+ obj._on_change_fields = defaultdict(list)
+
+ def on_change_fields_for_function(function_name):
+ # se recorre arbol buscando el primero
+ for parent_cls in type(obj).__mro__:
+ parent_meth = getattr(parent_cls, function_name, None)
+ if not parent_meth:
+ continue
+
+ on_changes = getattr(parent_meth, 'on_changes', None)
+ if on_changes:
+ return on_changes
+ return []
# forzamos registros de campos al modelo
# al instanciar
@@ -28,7 +42,12 @@ class ModelBase(object, metaclass=ModelMeta):
if hasattr(v, 'default') and v.default is not None:
setattr(obj, key, v.default)
-
+ # register callbacks for changes
+ function_name = 'on_change_%s' % (key)
+ on_change_fields = on_change_fields_for_function(function_name)
+ for field in on_change_fields:
+ obj._on_change_fields[field].append(function_name)
+
return obj
def _set_attribute(self, field, name, value):
diff --git a/facho/model/fields/__init__.py b/facho/model/fields/__init__.py
index 4e35a11..c3f59a3 100644
--- a/facho/model/fields/__init__.py
+++ b/facho/model/fields/__init__.py
@@ -6,3 +6,15 @@ from .virtual import Virtual
from .field import Field
__all__ = [Attribute, Many2One, Model, Virtual, Field]
+
+def on_change(fields):
+ from functools import wraps
+
+ def decorator(func):
+ setattr(func, 'on_changes', fields)
+
+ @wraps(func)
+ def wrapper(self, *arg, **kwargs):
+ return func(self, *arg, **kwargs)
+ return wrapper
+ return decorator
diff --git a/facho/model/fields/attribute.py b/facho/model/fields/attribute.py
index 9d8677c..6adfc36 100644
--- a/facho/model/fields/attribute.py
+++ b/facho/model/fields/attribute.py
@@ -16,4 +16,6 @@ class Attribute(Field):
def __set__(self, inst, value):
assert self.name is not None
self.value = value
+
+ self._changed_field(inst, self.name, value)
inst._set_attribute(self.name, self.attribute, value)
diff --git a/facho/model/fields/field.py b/facho/model/fields/field.py
index 6d8e523..d340bbc 100644
--- a/facho/model/fields/field.py
+++ b/facho/model/fields/field.py
@@ -39,3 +39,8 @@ class Field:
self._set_namespace(obj, self.namespace, inst.__namespace__)
inst._fields[self.name] = obj
return obj
+
+ def _changed_field(self, inst, name, value):
+ for fun in inst._on_change_fields[name]:
+ getattr(inst, fun)(name, value)
+
diff --git a/facho/model/fields/function.py b/facho/model/fields/function.py
index 58388e5..1da5e52 100644
--- a/facho/model/fields/function.py
+++ b/facho/model/fields/function.py
@@ -31,4 +31,5 @@ class Function(Field):
def __set__(self, inst, value):
inst._set_field(self.name, self.field)
+ self._changed_field(inst, self.name, value)
self.field.__set__(inst, value)
diff --git a/facho/model/fields/many2one.py b/facho/model/fields/many2one.py
index d75e03b..5134e3d 100644
--- a/facho/model/fields/many2one.py
+++ b/facho/model/fields/many2one.py
@@ -25,6 +25,7 @@ class Many2One(Field):
setter(inst_model, value)
else:
inst_model._set_content(value)
-
+
+ self._changed_field(inst, self.name, value)
diff --git a/tests/test_model.py b/tests/test_model.py
index 1028803..532c582 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -364,3 +364,38 @@ def test_field_inserted_default_nested_many2one():
person = Person()
assert 'ole' == person.to_xml()
+def test_model_on_change_field():
+ class Hash(facho.model.Model):
+ __name__ = 'Hash'
+
+ class Person(facho.model.Model):
+ __name__ = 'Person'
+
+ react = fields.Attribute('react')
+ hash = fields.Many2One(Hash)
+
+ @fields.on_change(['hash'])
+ def on_change_react(self, name, value):
+ assert name == 'hash'
+ self.react = "%s+4" % (value)
+
+ person = Person()
+ person.hash = 'hola'
+ assert 'hola' == person.to_xml()
+
+def test_model_on_change_field_attribute():
+ class Person(facho.model.Model):
+ __name__ = 'Person'
+
+ react = fields.Attribute('react')
+ hash = fields.Attribute('Hash')
+
+ @fields.on_change(['hash'])
+ def on_change_react(self, name, value):
+ assert name == 'hash'
+ self.react = "%s+4" % (value)
+
+ person = Person()
+ person.hash = 'hola'
+ assert '' == person.to_xml()
+