From 21cc7ed1b4e315c5e757620ca921da1b343dca6d Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 5 Aug 2005 02:33:56 +0000 Subject: [PATCH] --- doc/build/content/adv_datamapping.myt | 3 + doc/build/content/datamapping.myt | 3 + doc/build/content/dbengine.myt | 7 +++ doc/build/content/document_base.myt | 10 +++- doc/build/content/metadata.myt | 7 +++ doc/build/content/pooling.myt | 3 + doc/build/content/roadmap.myt | 39 ++++++++++++ doc/build/content/sqlconstruction.myt | 3 + doc/style.css | 2 +- lib/sqlalchemy/mapper.py | 86 ++++++++++++++++++++++++--- lib/sqlalchemy/sql.py | 7 ++- 11 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 doc/build/content/adv_datamapping.myt create mode 100644 doc/build/content/datamapping.myt create mode 100644 doc/build/content/dbengine.myt create mode 100644 doc/build/content/metadata.myt create mode 100644 doc/build/content/pooling.myt create mode 100644 doc/build/content/roadmap.myt create mode 100644 doc/build/content/sqlconstruction.myt diff --git a/doc/build/content/adv_datamapping.myt b/doc/build/content/adv_datamapping.myt new file mode 100644 index 0000000000..c3d532417d --- /dev/null +++ b/doc/build/content/adv_datamapping.myt @@ -0,0 +1,3 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="adv_datamapping", description="Advanced Data Mapping" &> + \ No newline at end of file diff --git a/doc/build/content/datamapping.myt b/doc/build/content/datamapping.myt new file mode 100644 index 0000000000..56c10375ba --- /dev/null +++ b/doc/build/content/datamapping.myt @@ -0,0 +1,3 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="datamapping", description="Basic Data Mapping" &> + \ No newline at end of file diff --git a/doc/build/content/dbengine.myt b/doc/build/content/dbengine.myt new file mode 100644 index 0000000000..929059e4c7 --- /dev/null +++ b/doc/build/content/dbengine.myt @@ -0,0 +1,7 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="dbengine", description="Database Engines" &> + <&|doclib.myt:item, name="establishing", description="Establishing a Database Engine" &> + + <&|doclib.myt:item, name="options", description="Database Engine Options" &> + + \ No newline at end of file diff --git a/doc/build/content/document_base.myt b/doc/build/content/document_base.myt index 35c41d545f..6a692d623b 100644 --- a/doc/build/content/document_base.myt +++ b/doc/build/content/document_base.myt @@ -3,7 +3,15 @@ <%python scope="global"> files = [ - 'coolthings' + 'roadmap', + 'pooling', + 'dbengine', + 'metadata', + 'sqlconstruction', + 'datamapping', + 'adv_datamapping', + 'activerecord', + ] diff --git a/doc/build/content/metadata.myt b/doc/build/content/metadata.myt new file mode 100644 index 0000000000..4468c776fc --- /dev/null +++ b/doc/build/content/metadata.myt @@ -0,0 +1,7 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="metadata", description="Database Meta Data" &> + <&|doclib.myt:item, name="tables", description="Describing Tables with Database Meta Data" &> + + <&|doclib.myt:item, name="building", description="Building and Dropping Database Tables" &> + + \ No newline at end of file diff --git a/doc/build/content/pooling.myt b/doc/build/content/pooling.myt new file mode 100644 index 0000000000..e76c369593 --- /dev/null +++ b/doc/build/content/pooling.myt @@ -0,0 +1,3 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="pooling", description="Connection Pooling" &> + \ No newline at end of file diff --git a/doc/build/content/roadmap.myt b/doc/build/content/roadmap.myt new file mode 100644 index 0000000000..deed372463 --- /dev/null +++ b/doc/build/content/roadmap.myt @@ -0,0 +1,39 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="roadmap", description="Roadmap" &> +

SQLAlchemy includes several components, each of which are useful by themselves to give varying levels of assistance to a database-enabled application. Below is a roadmap of the "knowledge dependencies" between these components indicating the order in which concepts may be learned. +

+ +
+Start
+  |
+  |
+  |--- Establishing Transparent Connection Pooling
+  |              |
+  |              |
+  |              |------ Connection Pooling Configuration
+  |                                         |              
+  |                                         |
+  |--- <&formatting.myt:link, path="dbengine_establishing" &>       |
+                   |                        |
+                   |                        | 
+                   |--------- <&formatting.myt:link, path="dbengine_options" &>
+                   |
+                   |
+                   |---- <&formatting.myt:link, path="metadata_tables" &>
+                                   |
+                                   |
+                                   |---- <&formatting.myt:link, path="metadata_building" &>
+                                   | 
+                                   |    
+                                   |---- <&formatting.myt:link, path="sql" &>
+                                   |                                      |                
+                                   |                                      |                                  
+                                   |---- Basic Data Mapping               |                
+                                   |               |                      |  
+                                   |               |                      |              
+                                   |               |----------- Advanced Data Mapping
+                                   |                                        
+                                   |                
+                                   |----- Basic Active Record
+
+ \ No newline at end of file diff --git a/doc/build/content/sqlconstruction.myt b/doc/build/content/sqlconstruction.myt new file mode 100644 index 0000000000..8bd903ff40 --- /dev/null +++ b/doc/build/content/sqlconstruction.myt @@ -0,0 +1,3 @@ +<%flags>inherit='document_base.myt' +<&|doclib.myt:item, name="sql", description="Constructing SQL Queries via Python Expressions" &> + \ No newline at end of file diff --git a/doc/style.css b/doc/style.css index 5471ead18e..64090d6395 100644 --- a/doc/style.css +++ b/doc/style.css @@ -1,4 +1,4 @@ -body, td { +body, td, .normaltype { font-family: verdana, sans-serif; font-size: 12px; } diff --git a/lib/sqlalchemy/mapper.py b/lib/sqlalchemy/mapper.py index 8e7b04473f..28d42792f8 100644 --- a/lib/sqlalchemy/mapper.py +++ b/lib/sqlalchemy/mapper.py @@ -49,8 +49,14 @@ def relation_loader(mapper, secondary = None, primaryjoin = None, secondaryjoin def relation_mapper(class_, selectable, secondary = None, primaryjoin = None, secondaryjoin = None, table = None, properties = None, lazy = True, **options): return relation_loader(mapper(class_, selectable, table = table, properties = properties, isroot = False, **options), secondary, primaryjoin, secondaryjoin, lazy = lazy, **options) +_mappers = {} def mapper(*args, **params): - return Mapper(*args, **params) + hashkey = mapper_hash_key(*args, **params) + print "HASHKEY: " + hashkey + try: + return _mappers[hashkey] + except KeyError: + return _mappers.setdefault(hashkey, Mapper(*args, **params)) def identitymap(): return IdentityMap() @@ -84,6 +90,7 @@ class Mapper(object): else: self.identitymap = _global_identitymap + self.properties = properties if properties is not None: for key, value in properties.iteritems(): self.props[key] = value @@ -91,6 +98,17 @@ class Mapper(object): if isroot: self.init(self) + def hash_key(self): + return mapper_hash_key( + self.class_, + self.selectable, + self.table, + self.properties, + self.identitymap, + self.use_smart_properties, + self.echo + ) + def set_property(self, key, prop): self.props[key] = prop prop.init(key, self, self.root) @@ -125,12 +143,17 @@ class Mapper(object): def options(self, *options): """uses this mapper as a prototype for a new mapper with different behavior. - *options is a list of options directives, which include eagerload() and lazyload()""" - mapper = copy.copy(self) - for option in options: - option.process(mapper) - return mapper + + hashkey = hash_key(self) + "->" + repr([hash_key(o) for o in options]) + print "HASHKEY: " + hashkey + try: + return _mappers[hashkey] + except KeyError: + mapper = copy.copy(self) + for option in options: + option.process(mapper) + return _mappers.setdefault(hashkey, mapper) def select(self, arg = None, **params): """selects instances of the object from the database. @@ -253,6 +276,9 @@ class Mapper(object): class MapperOption: def process(self, mapper): raise NotImplementedError() + + def hash_key(self): + return repr(self) class MapperProperty: """an element attached to a Mapper that describes the loading and population @@ -262,6 +288,11 @@ class MapperProperty: to the row. """ raise NotImplementedError() + def hash_key(self): + """describes this property and its instantiated arguments in such a way + as to uniquely identify the concept this MapperProperty represents""" + raise NotImplementedError() + def setup(self, key, primarytable, statement, **options): """called when a statement is being constructed. """ return self @@ -281,6 +312,9 @@ class ColumnProperty(MapperProperty): def __init__(self, column): self.column = column + def hash_key(self): + return "ColumnProperty(%s)" % hash_key(self.column) + def init(self, key, parent, root): self.key = key if root.use_smart_properties: @@ -298,14 +332,42 @@ class ColumnProperty(MapperProperty): setattr(instance, self.key, row[self.column.label]) +def hash_key(obj): + if obj is None: + return 'None' + else: + return obj.hash_key() + +def mapper_hash_key(class_, selectable, table = None, properties = None, identitymap = None, use_smart_properties = True, isroot = True, echo = None): + if properties is None: + properties = {} + return ( + "Mapper(%s, %s, table=%s, properties=%s, identitymap=%s, use_smart_properties=%s, echo=%s)" % ( + repr(class_), + hash_key(selectable), + hash_key(table), + repr(dict([(k, hash_key(p)) for k,p in properties.iteritems()])), + hash_key(identitymap), + repr(use_smart_properties), + repr(echo) + + ) + ) + + + class PropertyLoader(MapperProperty): - def __init__(self, mapper, secondary, primaryjoin, secondaryjoin, **options): + def __init__(self, mapper, secondary, primaryjoin, secondaryjoin): self.mapper = mapper self.target = self.mapper.selectable self.secondary = secondary self.primaryjoin = primaryjoin self.secondaryjoin = secondaryjoin - + self._hash_key = "%s(%s, %s, %s, %s)" % (self.__class__.__name__, hash_key(mapper), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin)) + + def hash_key(self): + return self._hash_key + def init(self, key, parent, root): self.key = key self.mapper.init(root) @@ -369,7 +431,8 @@ class EagerLoader(PropertyLoader): if self.secondaryjoin is not None: [self.to_alias.append(f) for f in self.secondaryjoin._get_from_objects()] del self.to_alias[parent.selectable] - + + def setup(self, key, primarytable, statement, **options): """add a left outer join to the statement thats being constructed""" @@ -415,6 +478,9 @@ class EagerLazySwitcher(MapperOption): self.key = key self.toeager = toeager + def hash_key(self): + return "EagerLazySwitcher(%s, %s)" % (repr(self.key), repr(self.toeager)) + def process(self, mapper): oldprop = mapper.props[self.key] if self.toeager: @@ -492,6 +558,8 @@ def match_primaries(primary, secondary): class IdentityMap(dict): def get_key(self, row, class_, table, selectable): return (class_, table, tuple([row[column.label] for column in selectable.primary_keys])) + def hash_key(self): + return "IdentityMap(%s)" % id(self) _global_identitymap = IdentityMap() diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index 062ae00149..2c133f6c16 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -258,7 +258,7 @@ class CompoundClause(ClauseElement): return self.fromobj def hash_key(self): - return string.join(self.clauses.hash_key(), self.operator) + return string.join([c.hash_key() for c in self.clauses], self.operator) class ClauseList(ClauseElement): def __init__(self, *clauses): @@ -361,7 +361,10 @@ class Alias(Selectable): co._make_proxy(self) primary_keys = property (lambda self: [c for c in self.columns if c.primary_key]) - + + def hash_key(self): + return "Alias(%s, %s)" % (repr(self.selectable.hash_key()), repr(self.name)) + def accept_visitor(self, visitor): self.selectable.accept_visitor(visitor) visitor.visit_alias(self) -- 2.47.2