From f279e2b1f24bd6efc7542707707ea9724f679e65 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 9 Dec 2006 04:00:35 +0000 Subject: =?UTF-8?q?-=20added=20onupdate=20and=20ondelete=20keyword=20argum?= =?UTF-8?q?ents=20to=20ForeignKey;=20propigate=20to=20underlying=20Foreign?= =?UTF-8?q?KeyConstraint=20if=20present.=20=20(dont=20propigate=20in=20the?= =?UTF-8?q?=20other=20direction,=20however)=20-=20patched=20attribute=20sp?= =?UTF-8?q?eed=20enhancement=20[ticket:389]=20courtesy=20S=C3=A9bastien=20?= =?UTF-8?q?Lelong?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sqlalchemy/orm/attributes.py | 56 ++++++++++++++++++++++++++++------------ lib/sqlalchemy/schema.py | 6 +++-- 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'lib/sqlalchemy') diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 915ccd209..17f909613 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -609,8 +609,19 @@ class AttributeHistory(object): class AttributeManager(object): """allows the instrumentation of object attributes. AttributeManager is stateless, but can be - overridden by subclasses to redefine some of its factory operations.""" - + overridden by subclasses to redefine some of its factory operations. Also be aware AttributeManager + will cache attributes for a given class, allowing not to determine those for each objects (used + in managed_attributes() and noninherited_managed_attributes()). This cache is cleared for a given class + while calling register_attribute(), and can be cleared using clear_attribute_cache()""" + + def __init__(self): + # will cache attributes, indexed by class objects + self._inherited_attribute_cache = weakref.WeakKeyDictionary() + self._noninherited_attribute_cache = weakref.WeakKeyDictionary() + + def clear_attribute_cache(self): + self._attribute_cache.clear() + def rollback(self, *obj): """retrieves the committed history for each object in the given list, and rolls back the attributes each instance to their original value.""" @@ -636,24 +647,28 @@ class AttributeManager(object): for o in obj: o._state['original'] = CommittedState(self, o) o._state['modified'] = False - + def managed_attributes(self, class_): """returns an iterator of all InstrumentedAttribute objects associated with the given class.""" - if not isinstance(class_, type): - raise repr(class_) + " is not a type" - for key in dir(class_): - value = getattr(class_, key, None) - if isinstance(value, InstrumentedAttribute): - yield value + try: + return self._inherited_attribute_cache[class_] + except KeyError: + if not isinstance(class_, type): + raise TypeError(repr(class_) + " is not a type") + inherited = [v for v in [getattr(class_, key, None) for key in dir(class_)] if isinstance(v, InstrumentedAttribute)] + self._inherited_attribute_cache[class_] = inherited + return inherited def noninherited_managed_attributes(self, class_): - if not isinstance(class_, type): - raise repr(class_) + " is not a type" - for key in list(class_.__dict__): - value = getattr(class_, key, None) - if isinstance(value, InstrumentedAttribute): - yield value - + try: + return self._noninherited_attribute_cache[class_] + except KeyError: + if not isinstance(class_, type): + raise TypeError(repr(class_) + " is not a type") + noninherited = [v for v in [getattr(class_, key, None) for key in list(class_.__dict__)] if isinstance(v, InstrumentedAttribute)] + self._noninherited_attribute_cache[class_] = noninherited + return noninherited + def is_modified(self, object): for attr in self.managed_attributes(object.__class__): if attr.check_mutable_modified(object): @@ -714,7 +729,9 @@ class AttributeManager(object): """removes all InstrumentedAttribute property objects from the given class.""" for attr in self.noninherited_managed_attributes(class_): delattr(class_, attr.key) - + self._inherited_attribute_cache.pop(class_,None) + self._noninherited_attribute_cache.pop(class_,None) + def is_class_managed(self, class_, key): """returns True if the given key correponds to an instrumented property on the given class.""" return hasattr(class_, key) and isinstance(getattr(class_, key), InstrumentedAttribute) @@ -733,6 +750,11 @@ class AttributeManager(object): def register_attribute(self, class_, key, uselist, callable_=None, **kwargs): """registers an attribute at the class level to be instrumented for all instances of the class.""" + # firt invalidate the cache for the given class + # (will be reconstituted as needed, while getting managed attributes) + self._inherited_attribute_cache.pop(class_,None) + self._noninherited_attribute_cache.pop(class_,None) + #print self, "register attribute", key, "for class", class_ if not hasattr(class_, '_state'): def _get_state(self): diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 8ebeaea27..93f4574bc 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -501,7 +501,7 @@ class ForeignKey(SchemaItem): One or more ForeignKey objects are used within a ForeignKeyConstraint object which represents the table-level constraint definition.""" - def __init__(self, column, constraint=None, use_alter=False, name=None): + def __init__(self, column, constraint=None, use_alter=False, name=None, onupdate=None, ondelete=None): """Construct a new ForeignKey object. "column" can be a schema.Column object representing the relationship, @@ -518,6 +518,8 @@ class ForeignKey(SchemaItem): self.constraint = constraint self.use_alter = use_alter self.name = name + self.onupdate = onupdate + self.ondelete = ondelete def __repr__(self): return "ForeignKey(%s)" % repr(self._get_colspec()) @@ -588,7 +590,7 @@ class ForeignKey(SchemaItem): self.parent = column if self.constraint is None and isinstance(self.parent.table, Table): - self.constraint = ForeignKeyConstraint([],[], use_alter=self.use_alter, name=self.name) + self.constraint = ForeignKeyConstraint([],[], use_alter=self.use_alter, name=self.name, onupdate=self.onupdate, ondelete=self.ondelete) self.parent.table.append_constraint(self.constraint) self.constraint._append_fk(self) -- cgit v1.2.1