]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
(no commit message)
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 17 Sep 2005 20:03:46 +0000 (20:03 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 17 Sep 2005 20:03:46 +0000 (20:03 +0000)
lib/sqlalchemy/attributes.py
lib/sqlalchemy/mapper.py
lib/sqlalchemy/objectstore.py
lib/sqlalchemy/util.py

index bf28111df7f0e9aef1f3310f7b578961584341ef..06ed30605ee0ce457f88910f5e7bfe82f677d5fd 100644 (file)
@@ -1,7 +1,27 @@
+# attributes.py - manages object attributes
+# Copyright (C) 2005 Michael Bayer mike_mp@zzzcomputing.com
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
 import sqlalchemy.util as util
 import weakref
 
 class SmartProperty(object):
+    """attaches AttributeManager functionality to the property accessors of a class.  all instances
+    of the class will retrieve and modify their properties via an AttributeManager."""
     def __init__(self, manager):
         self.manager = manager
     def attribute_registry(self):
@@ -26,35 +46,8 @@ class SmartProperty(object):
                 
         return property(get_prop, set_prop, del_prop)
 
-class ListElement(util.HistoryArraySet):
-    """overrides HistoryArraySet to mark the parent object as dirty when changes occur"""
-
-    def __init__(self, obj, key, items = None):
-        self.obj = obj
-        self.key = key
-        util.HistoryArraySet.__init__(self, items)
-        obj.__dict__[key] = self.data
-
-    def list_value_changed(self, obj, key, listval):
-        pass    
-
-    def setattr(self, value):
-        self.obj.__dict__[self.key] = value
-        self.set_data(value)
-    def delattr(self, value):
-        pass    
-    def _setrecord(self, item):
-        res = util.HistoryArraySet._setrecord(self, item)
-        if res:
-            self.list_value_changed(self.obj, self.key, self)
-        return res
-    def _delrecord(self, item):
-        res = util.HistoryArraySet._delrecord(self, item)
-        if res:
-            self.list_value_changed(self.obj, self.key, self)
-        return res
-
 class PropHistory(object):
+    """manages the value of a particular scalar attribute on a particular object instance."""
     # make our own NONE to distinguish from "None"
     NONE = object()
     def __init__(self, obj, key):
@@ -81,7 +74,7 @@ class PropHistory(object):
         else:
             return []
     def deleted_items(self):
-        if self.orig is not PropHistory.NONE:
+        if self.orig is not PropHistory.NONE and self.orig is not None:
             return [self.orig]
         else:
             return []
@@ -91,19 +84,43 @@ class PropHistory(object):
         else:
             return []
 
+class ListElement(util.HistoryArraySet):
+    """manages the value of a particular list-based attribute on a particular object instance."""
+    def __init__(self, obj, key, items = None):
+        self.obj = obj
+        self.key = key
+        util.HistoryArraySet.__init__(self, items)
+        obj.__dict__[key] = self.data
+
+    def list_value_changed(self, obj, key, listval):
+        pass    
+
+    def setattr(self, value):
+        self.obj.__dict__[self.key] = value
+        self.set_data(value)
+    def delattr(self, value):
+        pass    
+    def _setrecord(self, item):
+        res = util.HistoryArraySet._setrecord(self, item)
+        if res:
+            self.list_value_changed(self.obj, self.key, self)
+        return res
+    def _delrecord(self, item):
+        res = util.HistoryArraySet._delrecord(self, item)
+        if res:
+            self.list_value_changed(self.obj, self.key, self)
+        return res
+
+
 class AttributeManager(object):
+    """maintains a set of per-attribute history objects for a set of objects."""
     def __init__(self):
         self.attribute_history = {}
+
     def value_changed(self, obj, key, value):
         pass
-#        if hasattr(obj, '_instance_key'):
-#            self.register_dirty(obj)
-#        else:
-#            self.register_new(obj)
-
     def create_prop(self, key, uselist):
         return SmartProperty(self).property(key, uselist)
-        
     def create_list(self, obj, key, list_):
         return ListElement(obj, key, list_)
         
index 98f225403cee0121e50c3c18b23d0815e5be7b16..9ededa8dfb89ebcb06ad4ea5e64ea7b42f7d221c 100644 (file)
@@ -30,7 +30,7 @@ def relation(*args, **params):
     else:
         return relation_mapper(*args, **params)
 
-def relation_loader(mapper, secondary = None, primaryjoin = None, secondaryjoin = None, lazy = True, private = False, **options):
+def relation_loader(mapper, secondary = None, primaryjoin = None, secondaryjoin = None, lazy = True, **options):
     if lazy:
         return LazyLoader(mapper, secondary, primaryjoin, secondaryjoin, **options)
     else:
@@ -434,9 +434,9 @@ class ColumnProperty(MapperProperty):
 
 
 class PropertyLoader(MapperProperty):
-    """describes an object property that holds a list of items that correspond to a related
+    """describes an object property that holds a single item or list of items that correspond to a related
     database table."""
-    def __init__(self, mapper, secondary, primaryjoin, secondaryjoin, uselist = True, foreignkey = None):
+    def __init__(self, mapper, secondary, primaryjoin, secondaryjoin, uselist = True, foreignkey = None, private = False):
         self.uselist = uselist
         self.mapper = mapper
         self.target = self.mapper.selectable
@@ -444,6 +444,7 @@ class PropertyLoader(MapperProperty):
         self.primaryjoin = primaryjoin
         self.secondaryjoin = secondaryjoin
         self.foreignkey = foreignkey
+        self.private = private
         self._hash_key = "%s(%s, %s, %s, %s, %s, uselist=%s)" % (self.__class__.__name__, hash_key(mapper), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin), hash_key(foreignkey), repr(self.uselist))
             
     def hash_key(self):
@@ -558,6 +559,8 @@ class PropertyLoader(MapperProperty):
                     self.primaryjoin.accept_visitor(setter)
                     self.secondaryjoin.accept_visitor(setter)
                     secondary_delete.append(associationrow)
+                    if self.private:
+                        uowcommit.add_item_to_delete(obj)
                 uowcommit.register_saved_list(childlist)
             if len(secondary_delete):
                 statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c]))
@@ -568,23 +571,33 @@ class PropertyLoader(MapperProperty):
         elif self.foreignkey.table == self.target:
             for obj in deplist:
                 childlist = getlist(obj)
+                clearkeys = False
                 for child in childlist.added_items():
                     associationrow = {}
                     self.primaryjoin.accept_visitor(setter)
                     uowcommit.register_saved_list(childlist)
-                # TODO: deleted items
+                clearkeys = True
+                for child in childlist.deleted_items():
+                     associationrow = {}
+                     self.primaryjoin.accept_visitor(setter)
+                     uowcommit.register_saved_list(childlist)
+                     if self.private:
+                         uowcommit.add_item_to_delete(child)
         elif self.foreignkey.table == self.parent.table:
             for child in deplist:
                 childlist = getlist(child)
-                try:
-                    print "got a list and its " + repr(childlist)
-                except:
-                    pass
+                clearkeys = False
                 for obj in childlist.added_items():
                     associationrow = {}
                     self.primaryjoin.accept_visitor(setter)
                     uowcommit.register_saved_list(childlist)
-                # TODO: deleted items
+                clearkeys = True
+                for obj in childlist.deleted_items():
+                    if self.private:
+                        uowcommit.add_item_to_delete(obj)
+                    associationrow = {}
+                    self.primaryjoin.accept_visitor(setter)
+                    uowcommit.register_saved_list(childlist)
         else:
             raise " no foreign key ?"
 
index 8d46fd6b6b2607dc98abe4fb27962c80e557a01c..7038c165763a1c95bdf254bcbf8ffcd9e0477ea1 100644 (file)
@@ -166,7 +166,7 @@ class UnitOfWork(object):
             return True
         
     def register_deleted(self, obj):
-        pass   
+        self.deleted.append(obj)  
 
     # TODO: tie in register_new/register_dirty with table transaction begins ?
     def begin(self):
@@ -180,14 +180,19 @@ class UnitOfWork(object):
 
         if len(objects):
             for obj in objects:
-                commit_context.append_task(obj)
+                if self.deleted.contains(obj):
+                    commit_context.add_item_to_delete(obj)
+                elif self.new.contains(obj) or self.dirty.contains(obj):
+                    commit_context.append_task(obj)
         else:
             for obj in [n for n in self.new] + [d for d in self.dirty]:
                 commit_context.append_task(obj)
             for item in self.modified_lists:
                 obj = item.obj
                 commit_context.append_task(obj)
-
+            for obj in self.deleted:
+                commit_context.add_item_to_delete(obj)
+                
         engines = util.HashSet()
         for mapper in commit_context.mappers:
             for e in mapper.engines:
@@ -227,6 +232,8 @@ class UOWTransaction(object):
         self.tasks = {}
         self.saved_objects = util.HashSet()
         self.saved_lists = util.HashSet()
+        self.deleted_objects = util.HashSet()
+        self.todelete = util.HashSet()
 
     def append_task(self, obj):
         mapper = self.object_mapper(obj)
@@ -252,6 +259,12 @@ class UOWTransaction(object):
     def register_saved_list(self, listobj):
         self.saved_lists.append(listobj)
 
+    def register_deleted(self, obj):
+        self.deleted_objects.append(obj)
+        
+    def add_item_to_delete(self, obj):
+        self.todelete.append(obj)
+        
     def object_mapper(self, obj):
         import sqlalchemy.mapper
         try:
index ae0442c16a44451eed7670b44fd8b6f46721c031..38b371a263786037be565ce6d19f41d7d88a31a6 100644 (file)
@@ -19,7 +19,9 @@ __ALL__ = ['OrderedProperties', 'OrderedDict']
 import thread, weakref, UserList
 
 class OrderedProperties(object):
-
+    """an object that maintains the order in which attributes are set upon it.
+    also provides an iterator and a very basic dictionary interface to those attributes.
+    """
     def __init__(self):
         self.__dict__['_list'] = []
             
@@ -87,6 +89,7 @@ class OrderedDict(dict):
         return dict.__getitem__(self, key)
 
 class ThreadLocal(object):
+    """an object in which attribute access occurs only within the context of the current thread"""
     def __init__(self):
         object.__setattr__(self, 'tdict', {})
     def __getattribute__(self, key):
@@ -98,6 +101,7 @@ class ThreadLocal(object):
         object.__getattribute__(self, 'tdict')["%d_%s" % (thread.get_ident(), key)] = value
 
 class HashSet(object):
+    """implements a Set."""
     def __init__(self, iter = None):
         self.map  = {}
         if iter is not None:
@@ -128,6 +132,9 @@ class HashSet(object):
         return self.map[key]
         
 class HistoryArraySet(UserList.UserList):
+    """extends a UserList to provide unique-set functionality as well as history-aware 
+    functionality, including information about what list elements were modified, 
+    as well as rollback capability."""
     def __init__(self, data = None):
         # stores the array's items as keys, and a value of True, False or None indicating
         # added, deleted, or unchanged for that item
@@ -247,6 +254,8 @@ class HistoryArraySet(UserList.UserList):
 
         
 class ScopedRegistry(object):
+    """a Registry that can store one or multiple instances of a single class 
+    on a per-application or per-thread scoped basis"""
     def __init__(self, createfunc, defaultscope):
         self.createfunc = createfunc
         self.defaultscope = defaultscope