0.3.2
- major connection pool bug fixed. fixes MySQL out of sync
errors, will also prevent transactions getting rolled back
-accidentally [ticket:387]
-- major speed enhancements vs. 0.3.1
+accidentally in all DBs [ticket:387]
+- major speed enhancements vs. 0.3.1, to bring speed
+back to 0.2.8 levels
- made conditional dozens of debug log calls that were
time-intensive to generate log messages
- fixed bug in cascade rules whereby the entire object graph
could be unnecessarily cascaded on the save/update cascade
+ - various speedups in attributes module
- identity map in Session is by default *no longer weak referencing*.
to have it be weak referencing, use create_session(weak_identity_map=True)
- MySQL detects errors 2006 (server has gone away) and 2014
[changeset:2110]
- added label() function to Select class, when scalar=True is used
to create a scalar subquery.
+- added onupdate and ondelete keyword arguments to ForeignKey; propigate
+to underlying ForeignKeyConstraint if present. (dont propigate in the
+other direction, however)
- fix to session.update() to preserve "dirty" status of incoming object
- sending a selectable to an IN no longer creates a "union" out of multiple
selects; only one selectable to an IN is allowed now (make a union yourself
- improved support for disabling save-update cascade via cascade="none" etc.
- added "remote_side" argument to relation(), used only with self-referential
mappers to force the direction of the parent/child relationship. replaces
-the usage of the "foreignkey" parameter for "switching" the direction;
-while "foreignkey" can still be used to "switch" the direction of a parent/
-child relationship, this usage is deprecated; "foreignkey" should always
-indicate the actual foreign key columns from now on.
+the usage of the "foreignkey" parameter for "switching" the direction.
+"foreignkey" argument is deprecated for all uses and will eventually
+be replaced by an argument dedicated to ForeignKey specification;
0.3.1
- Engine/Pool:
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."""
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):
"""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)
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):
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,
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())
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)