summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-12-09 04:00:35 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-12-09 04:00:35 +0000
commitf279e2b1f24bd6efc7542707707ea9724f679e65 (patch)
tree607fbadf483ba2652b41f4a9881bc0e6a1b55d4a
parentd56a50eb7056cfe054c03b47db3d79dab03a5638 (diff)
downloadsqlalchemy-f279e2b1f24bd6efc7542707707ea9724f679e65.tar.gz
- added onupdate and ondelete keyword arguments to ForeignKey; propigate
to underlying ForeignKeyConstraint if present. (dont propigate in the other direction, however) - patched attribute speed enhancement [ticket:389] courtesy Sébastien Lelong
-rw-r--r--CHANGES16
-rw-r--r--lib/sqlalchemy/orm/attributes.py56
-rw-r--r--lib/sqlalchemy/schema.py6
3 files changed, 53 insertions, 25 deletions
diff --git a/CHANGES b/CHANGES
index 21a2307cd..12e101670 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,12 +1,14 @@
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
@@ -18,6 +20,9 @@ to have it be weak referencing, use create_session(weak_identity_map=True)
[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
@@ -25,10 +30,9 @@ if union is needed; explicit better than implicit, dont guess, etc.)
- 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:
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)