]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added onupdate and ondelete keyword arguments to ForeignKey; propigate
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Dec 2006 04:00:35 +0000 (04:00 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Dec 2006 04:00:35 +0000 (04:00 +0000)
to underlying ForeignKeyConstraint if present.  (dont propigate in the
other direction, however)
- patched attribute speed enhancement [ticket:389] courtesy Sébastien Lelong

CHANGES
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/schema.py

diff --git a/CHANGES b/CHANGES
index 21a2307cd18a76782dcf28597ccaa1c407d1aee9..12e101670b7c9e4c98e523d077ceed6881cc6610 100644 (file)
--- 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:
index 915ccd209fcac21bedfe7951604c3451a1f01708..17f90961307ae7133b8bbc5ae52bf467d85c2654 100644 (file)
@@ -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):
index 8ebeaea27ddb7e520d8901754c814a53c5031cd8..93f4574bc1c43e76e4d2ff6797fb1f5b70381a0f 100644 (file)
@@ -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)