From 47a0dd33e2357bd38d2a45e1f7c86ae43a75ff0f Mon Sep 17 00:00:00 2001 From: bit4bit Date: Sat, 26 Jun 2021 21:28:02 +0000 Subject: [PATCH] se adiciona notificaciones de cambios para fields.One2Many FossilOrigin-Name: b422aa912c7c7873edcbbecf1914e9ff21a24cab3fc7e2a788e00efc16fe2f51 --- facho/model/fields/one2many.py | 56 ++++++++++++++++++++++++++++------ tests/test_model.py | 26 ++++++++++++++++ 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/facho/model/fields/one2many.py b/facho/model/fields/one2many.py index 571f61d..231874b 100644 --- a/facho/model/fields/one2many.py +++ b/facho/model/fields/one2many.py @@ -1,11 +1,46 @@ from .field import Field -class BoundModel: - def __init__(self, creator): +# TODO(bit4bit) lograr que isinstance se aplique +# al objeto envuelto +class _RelationProxy(): + def __init__(self, obj, inst, attribute): + self.__dict__['_obj'] = obj + self.__dict__['_inst'] = inst + self.__dict__['_attribute'] = attribute + + def __getattr__(self, name): + if (name in self.__dict__): + return self.__dict__[name] + + return getattr(self.__dict__['_obj'], name) + + def __setattr__(self, attr, value): + # TODO(bit4bit) hacemos proxy al sistema de notificacion de cambios + # algo burdo, se usa __dict__ para saltarnos el __getattr__ y generar un fallo por recursion + for fun in self.__dict__['_inst']._on_change_fields[self.__dict__['_attribute']]: + fun(self.__dict__['_inst'], self.__dict__['_attribute'], value) + + return setattr(self._obj, attr, value) + +class _Relation(): + def __init__(self, creator, inst, attribute): self.creator = creator + self.inst = inst + self.attribute = attribute + self.relations = [] def create(self): - return self.creator() + n_relations = len(self.relations) + attribute = '%s_%d' % (self.attribute, n_relations) + relation = self.creator(attribute) + proxy = _RelationProxy(relation, self.inst, self.attribute) + + self.relations.append(relation) + return proxy + + def __len__(self): + return len(self.relations) + class One2Many(Field): def __init__(self, model, name=None, namespace=None, default=None): @@ -13,13 +48,16 @@ class One2Many(Field): self.field_name = name self.namespace = namespace self.default = default - self.count_relations = 0 + self.relation = None def __get__(self, inst, cls): assert self.name is not None - def creator(): - attribute = '%s_%d' % (self.name, self.count_relations) - self.count_relations += 1 - return self._create_model(inst, name=self.field_name, model=self.model, attribute=attribute) - return BoundModel(creator) + def creator(attribute): + return self._create_model(inst, name=self.field_name, model=self.model, attribute=attribute) + + if self.relation: + return self.relation + else: + self.relation = _Relation(creator, inst, self.name) + return self.relation diff --git a/tests/test_model.py b/tests/test_model.py index 34f972b..159b5d2 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -416,3 +416,29 @@ def test_model_one2many(): line = invoice.lines.create() line.quantity = 5 assert '' == invoice.to_xml() + + +def test_model_one2many_with_on_changes(): + class Line(facho.model.Model): + __name__ = 'Line' + + quantity = fields.Attribute('quantity') + + class Invoice(facho.model.Model): + __name__ = 'Invoice' + + lines = fields.One2Many(Line) + count = fields.Attribute('count', default=0) + + @fields.on_change(['lines']) + def refresh_count(self, name, value): + self.count = len(self.lines) + + invoice = Invoice() + line = invoice.lines.create() + line.quantity = 3 + line = invoice.lines.create() + line.quantity = 5 + + assert len(invoice.lines) == 2 + assert '' == invoice.to_xml()