mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-02 05:48:52 -04:00
This commit is contained in:
@@ -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_)
|
||||
|
||||
|
||||
@@ -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 ?"
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
+10
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user