summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2005-09-17 20:03:46 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2005-09-17 20:03:46 +0000
commit5ca6c68b8dcecd8f0d52ff4ed93d78998ac41eda (patch)
tree0eb3c7f27f904a9e6bbfcaf29eb460287587a6ce /lib
parent66444e0cfb427cc9a0593c62245ea3fac07584e6 (diff)
downloadsqlalchemy-5ca6c68b8dcecd8f0d52ff4ed93d78998ac41eda.tar.gz
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/attributes.py87
-rw-r--r--lib/sqlalchemy/mapper.py31
-rw-r--r--lib/sqlalchemy/objectstore.py19
-rw-r--r--lib/sqlalchemy/util.py11
4 files changed, 100 insertions, 48 deletions
diff --git a/lib/sqlalchemy/attributes.py b/lib/sqlalchemy/attributes.py
index bf28111df..06ed30605 100644
--- a/lib/sqlalchemy/attributes.py
+++ b/lib/sqlalchemy/attributes.py
@@ -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_)
diff --git a/lib/sqlalchemy/mapper.py b/lib/sqlalchemy/mapper.py
index 98f225403..9ededa8df 100644
--- a/lib/sqlalchemy/mapper.py
+++ b/lib/sqlalchemy/mapper.py
@@ -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 ?"
diff --git a/lib/sqlalchemy/objectstore.py b/lib/sqlalchemy/objectstore.py
index 8d46fd6b6..7038c1657 100644
--- a/lib/sqlalchemy/objectstore.py
+++ b/lib/sqlalchemy/objectstore.py
@@ -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:
diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py
index ae0442c16..38b371a26 100644
--- a/lib/sqlalchemy/util.py
+++ b/lib/sqlalchemy/util.py
@@ -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