diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-01-02 14:23:42 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-01-02 14:23:42 -0500 |
| commit | 350aed3fdb9f1e73e69655e53f44ca6a91c196da (patch) | |
| tree | 3d2a128667b5f6ca6d0b4e1f4865fc98aac6b60b /examples | |
| parent | 71f92436bdc86f30e2c21d8f5244733601e8c39e (diff) | |
| download | sqlalchemy-350aed3fdb9f1e73e69655e53f44ca6a91c196da.tar.gz | |
- whitespace removal bonanza
Diffstat (limited to 'examples')
28 files changed, 288 insertions, 288 deletions
diff --git a/examples/adjacency_list/adjacency_list.py b/examples/adjacency_list/adjacency_list.py index 624239869..bac43f097 100644 --- a/examples/adjacency_list/adjacency_list.py +++ b/examples/adjacency_list/adjacency_list.py @@ -1,9 +1,9 @@ from sqlalchemy import MetaData, Table, Column, Sequence, ForeignKey,\ Integer, String, create_engine - + from sqlalchemy.orm import sessionmaker, mapper, relationship, backref,\ joinedload_all - + from sqlalchemy.orm.collections import attribute_mapped_collection metadata = MetaData() @@ -18,38 +18,38 @@ class TreeNode(object): def __init__(self, name, parent=None): self.name = name self.parent = parent - + def append(self, nodename): self.children[nodename] = TreeNode(nodename, parent=self) - + def __repr__(self): return "TreeNode(name=%r, id=%r, parent_id=%r)" % ( self.name, self.id, self.parent_id ) - + def dump_tree(node, indent=0): - + return " " * indent + repr(node) + \ "\n" + \ "".join([ dump_tree(c, indent +1) for c in node.children.values()] ) - + mapper(TreeNode, tree_table, properties={ 'children': relationship(TreeNode, # cascade deletions cascade="all", - + # many to one + adjacency list - remote_side # is required to reference the 'remote' # column in the join condition. backref=backref("parent", remote_side=tree_table.c.id), - + # children will be represented as a dictionary # on the "name" attribute. collection_class=attribute_mapped_collection('name'), diff --git a/examples/association/basic_association.py b/examples/association/basic_association.py index d3d764167..19b304f9a 100644 --- a/examples/association/basic_association.py +++ b/examples/association/basic_association.py @@ -60,7 +60,7 @@ class OrderItem(object): def __init__(self, item, price=None): self.item = item self.price = price or item.price - + mapper(Order, orders, properties={ 'order_items': relationship(OrderItem, cascade="all, delete-orphan", backref='order') @@ -82,7 +82,7 @@ session.commit() # function to return items from the DB def item(name): return session.query(Item).filter_by(description=name).one() - + # create an order order = Order('john smith') diff --git a/examples/beaker_caching/__init__.py b/examples/beaker_caching/__init__.py index d7e97efea..cc9f71d8b 100644 --- a/examples/beaker_caching/__init__.py +++ b/examples/beaker_caching/__init__.py @@ -16,17 +16,17 @@ In this demo, the following techniques are illustrated: deep within an object graph when lazy loads occur. E.g.:: - + # query for Person objects, specifying cache q = Session.query(Person).options(FromCache("default", "all_people")) - + # specify that each Person's "addresses" collection comes from # cache too q = q.options(RelationshipCache("default", "by_person", Person.addresses)) - + # query print q.all() - + To run, both SQLAlchemy and Beaker (1.4 or greater) must be installed or on the current PYTHONPATH. The demo will create a local directory for datafiles, insert initial data, and run. Running the @@ -53,7 +53,7 @@ Listing of files: bootstrap fixture data if necessary. caching_query.py - Represent functions and classes - which allow the usage of Beaker caching with SQLAlchemy. + which allow the usage of Beaker caching with SQLAlchemy. Introduces a query option called FromCache. model.py - The datamodel, which represents Person that has multiple diff --git a/examples/beaker_caching/advanced.py b/examples/beaker_caching/advanced.py index 113060033..c16e02f33 100644 --- a/examples/beaker_caching/advanced.py +++ b/examples/beaker_caching/advanced.py @@ -13,13 +13,13 @@ from sqlalchemy.orm import joinedload def load_name_range(start, end, invalidate=False): """Load Person objects on a range of names. - + start/end are integers, range is then "person <start>" - "person <end>". - + The cache option we set up is called "name_range", indicating a range of names for the Person class. - + The `Person.addresses` collections are also cached. Its basically another level of tuning here, as that particular cache option can be transparently replaced with joinedload(Person.addresses). @@ -36,17 +36,17 @@ def load_name_range(start, end, invalidate=False): # have the "addresses" collection cached separately # each lazyload of Person.addresses loads from cache. q = q.options(RelationshipCache("default", "by_person", Person.addresses)) - + # alternatively, eagerly load the "addresses" collection, so that they'd # be cached together. This issues a bigger SQL statement and caches # a single, larger value in the cache per person rather than two # separate ones. #q = q.options(joinedload(Person.addresses)) - + # if requested, invalidate the cache on current criterion. if invalidate: q.invalidate() - + return q.all() print "two through twelve, possibly from cache:\n" diff --git a/examples/beaker_caching/caching_query.py b/examples/beaker_caching/caching_query.py index d9ec1576e..4af812500 100644 --- a/examples/beaker_caching/caching_query.py +++ b/examples/beaker_caching/caching_query.py @@ -16,7 +16,7 @@ The three new concepts introduced here are: The rest of what's here are standard SQLAlchemy and Beaker constructs. - + """ from sqlalchemy.orm.interfaces import MapperOption from sqlalchemy.orm.query import Query @@ -25,10 +25,10 @@ from sqlalchemy.sql import visitors class CachingQuery(Query): """A Query subclass which optionally loads full results from a Beaker cache region. - + The CachingQuery stores additional state that allows it to consult a Beaker cache before accessing the database: - + * A "region", which is a cache region argument passed to a Beaker CacheManager, specifies a particular cache configuration (including backend implementation, expiration times, etc.) @@ -36,13 +36,13 @@ class CachingQuery(Query): group of keys within the cache. A query that filters on a name might use the name "by_name", a query that filters on a date range to a joined table might use the name "related_date_range". - + When the above state is present, a Beaker cache is retrieved. - + The "namespace" name is first concatenated with a string composed of the individual entities and columns the Query requests, i.e. such as ``Query(User.id, User.name)``. - + The Beaker cache is then loaded from the cache manager based on the region and composed namespace. The key within the cache itself is then constructed against the bind parameters specified @@ -51,17 +51,17 @@ class CachingQuery(Query): The FromCache and RelationshipCache mapper options below represent the "public" method of configuring this state upon the CachingQuery. - + """ - + def __init__(self, manager, *args, **kw): self.cache_manager = manager Query.__init__(self, *args, **kw) - + def __iter__(self): """override __iter__ to pull results from Beaker if particular attributes have been configured. - + Note that this approach does *not* detach the loaded objects from the current session. If the cache backend is an in-process cache (like "memory") and lives beyond the scope of the current session's @@ -69,7 +69,7 @@ class CachingQuery(Query): modified to first expunge() each loaded item from the current session before returning the list of items, so that the items in the cache are not the same ones in the current Session. - + """ if hasattr(self, '_cache_parameters'): return self.get_value(createfunc=lambda: list(Query.__iter__(self))) @@ -99,13 +99,13 @@ class CachingQuery(Query): """Set the value in the cache for this query.""" cache, cache_key = _get_cache_parameters(self) - cache.put(cache_key, value) + cache.put(cache_key, value) def query_callable(manager): def query(*arg, **kw): return CachingQuery(manager, *arg, **kw) return query - + def _get_cache_parameters(query): """For a query with cache_region and cache_namespace configured, return the correspoinding Cache instance and cache key, based @@ -116,16 +116,16 @@ def _get_cache_parameters(query): raise ValueError("This Query does not have caching parameters configured.") region, namespace, cache_key = query._cache_parameters - + namespace = _namespace_from_query(namespace, query) - + if cache_key is None: # cache key - the value arguments from this query's parameters. args = _params_from_query(query) cache_key = " ".join([str(x) for x in args]) - + assert cache_key is not None, "Cache key was None !" - + # get cache cache = query.cache_manager.get_cache_region(namespace, region) @@ -146,7 +146,7 @@ def _namespace_from_query(namespace, query): return namespace def _set_cache_parameters(query, region, namespace, cache_key): - + if hasattr(query, '_cache_parameters'): region, namespace, cache_key = query._cache_parameters raise ValueError("This query is already configured " @@ -154,7 +154,7 @@ def _set_cache_parameters(query, region, namespace, cache_key): (region, namespace) ) query._cache_parameters = region, namespace, cache_key - + class FromCache(MapperOption): """Specifies that a Query should load results from a cache.""" @@ -162,14 +162,14 @@ class FromCache(MapperOption): def __init__(self, region, namespace, cache_key=None): """Construct a new FromCache. - + :param region: the cache region. Should be a region configured in the Beaker CacheManager. - + :param namespace: the cache namespace. Should be a name uniquely describing the target Query's lexical structure. - + :param cache_key: optional. A string cache key that will serve as the key to the query. Use this if your query has a huge amount of parameters (such @@ -180,10 +180,10 @@ class FromCache(MapperOption): self.region = region self.namespace = namespace self.cache_key = cache_key - + def process_query(self, query): """Process a Query during normal loading operation.""" - + _set_cache_parameters(query, self.region, self.namespace, self.cache_key) class RelationshipCache(MapperOption): @@ -194,18 +194,18 @@ class RelationshipCache(MapperOption): def __init__(self, region, namespace, attribute): """Construct a new RelationshipCache. - + :param region: the cache region. Should be a region configured in the Beaker CacheManager. - + :param namespace: the cache namespace. Should be a name uniquely describing the target Query's lexical structure. - + :param attribute: A Class.attribute which indicates a particular class relationship() whose lazy loader should be pulled from the cache. - + """ self.region = region self.namespace = namespace @@ -234,11 +234,11 @@ class RelationshipCache(MapperOption): def and_(self, option): """Chain another RelationshipCache option to this one. - + While many RelationshipCache objects can be specified on a single Query separately, chaining them together allows for a more efficient lookup during load. - + """ self._relationship_options.update(option._relationship_options) return self @@ -246,18 +246,18 @@ class RelationshipCache(MapperOption): def _params_from_query(query): """Pull the bind parameter values from a query. - + This takes into account any scalar attribute bindparam set up. - + E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7))) would return [5, 7]. - + """ v = [] def visit_bindparam(bind): - + if bind.key in query._params: - value = query._params[bind.key] + value = query._params[bind.key] elif bind.callable: # lazyloader may dig a callable in here, intended # to late-evaluate params after autoflush is called. @@ -265,7 +265,7 @@ def _params_from_query(query): value = bind.callable() else: value = bind.value - + v.append(value) if query._criterion is not None: visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam}) diff --git a/examples/beaker_caching/environment.py b/examples/beaker_caching/environment.py index ea946606c..740c5977a 100644 --- a/examples/beaker_caching/environment.py +++ b/examples/beaker_caching/environment.py @@ -35,7 +35,7 @@ if not os.path.exists(root): "Press enter to continue.\n" % root ) os.makedirs(root) - + dbfile = os.path.join(root, "beaker_demo.db") engine = create_engine('sqlite:///%s' % dbfile, echo=True) Session.configure(bind=engine) @@ -50,7 +50,7 @@ cache_manager.regions['default'] ={ 'type':'file', 'data_dir':root, 'expire':3600, - + # set start_time to current time # to re-cache everything # upon application startup diff --git a/examples/beaker_caching/fixture_data.py b/examples/beaker_caching/fixture_data.py index 67af746e3..09f020cea 100644 --- a/examples/beaker_caching/fixture_data.py +++ b/examples/beaker_caching/fixture_data.py @@ -19,7 +19,7 @@ def install(): ('New York', 'United States', ('10001', '10002', '10003', '10004', '10005', '10006')), ('San Francisco', 'United States', ('94102', '94103', '94104', '94105', '94107', '94108')) ] - + countries = {} all_post_codes = [] for city, country, postcodes in data: @@ -27,12 +27,12 @@ def install(): country = countries[country] except KeyError: countries[country] = country = Country(country) - + city = City(city, country) pc = [PostalCode(code, city) for code in postcodes] Session.add_all(pc) all_post_codes.extend(pc) - + for i in xrange(1, 51): person = Person( "person %.2d" % i, @@ -44,6 +44,6 @@ def install(): Session.add(person) Session.commit() - + # start the demo fresh Session.remove()
\ No newline at end of file diff --git a/examples/beaker_caching/local_session_caching.py b/examples/beaker_caching/local_session_caching.py index cce2835f1..b63858362 100644 --- a/examples/beaker_caching/local_session_caching.py +++ b/examples/beaker_caching/local_session_caching.py @@ -17,7 +17,7 @@ class ScopedSessionNamespace(container.MemoryNamespaceManager): When used with the query_cache system, the effect is that the objects in the cache are the same as that within the session - the merge() - is a formality that doesn't actually create a second instance. + is a formality that doesn't actually create a second instance. This makes it safe to use for updates of data from an identity perspective (still not ideal for deletes though). @@ -25,25 +25,25 @@ class ScopedSessionNamespace(container.MemoryNamespaceManager): is automatically disposed upon session.remove(). """ - + def __init__(self, namespace, scoped_session, **kwargs): """__init__ is called by Beaker itself.""" - + container.NamespaceManager.__init__(self, namespace) self.scoped_session = scoped_session - + @classmethod def create_session_container(cls, beaker_name, scoped_session): """Create a new session container for a given scoped_session.""" - + def create_namespace(namespace, **kw): return cls(namespace, scoped_session, **kw) cache.clsmap[beaker_name] = create_namespace - + @property def dictionary(self): """Return the cache dictionary used by this MemoryNamespaceManager.""" - + sess = self.scoped_session() try: nscache = sess._beaker_cache @@ -55,35 +55,35 @@ class ScopedSessionNamespace(container.MemoryNamespaceManager): if __name__ == '__main__': from environment import Session, cache_manager from caching_query import FromCache - + # create a Beaker container type called "ext:local_session". # it will reference the ScopedSession in meta. ScopedSessionNamespace.create_session_container("ext:local_session", Session) - + # set up a region based on this new container type. cache_manager.regions['local_session'] ={'type':'ext:local_session'} - + from model import Person - + # query to load Person by name, with criterion # of "person 10" q = Session.query(Person).\ options(FromCache("local_session", "by_name")).\ filter(Person.name=="person 10") - + # load from DB person10 = q.one() - + # next call, the query is cached. person10 = q.one() # clear out the Session. The "_beaker_cache" dictionary # disappears with it. Session.remove() - + # query calls from DB again person10 = q.one() - + # identity is preserved - person10 is the *same* object that's # ultimately inside the cache. So it is safe to manipulate # the not-queried-for attributes of objects when using such a diff --git a/examples/beaker_caching/model.py b/examples/beaker_caching/model.py index c139d3284..629b263a7 100644 --- a/examples/beaker_caching/model.py +++ b/examples/beaker_caching/model.py @@ -18,7 +18,7 @@ class Country(Base): id = Column(Integer, primary_key=True) name = Column(String(100), nullable=False) - + def __init__(self, name): self.name = name @@ -41,15 +41,15 @@ class PostalCode(Base): code = Column(String(10), nullable=False) city_id = Column(Integer, ForeignKey('city.id'), nullable=False) city = relationship(City) - + @property def country(self): return self.city.country - + def __init__(self, code, city): self.code = code self.city = city - + class Address(Base): __tablename__ = 'address' @@ -58,21 +58,21 @@ class Address(Base): street = Column(String(200), nullable=False) postal_code_id = Column(Integer, ForeignKey('postal_code.id')) postal_code = relationship(PostalCode) - + @property def city(self): return self.postal_code.city - + @property def country(self): return self.postal_code.country - + def __str__(self): return "%s\t"\ "%s, %s\t"\ "%s" % (self.street, self.city.name, self.postal_code.code, self.country.name) - + class Person(Base): __tablename__ = 'person' @@ -86,13 +86,13 @@ class Person(Base): def __str__(self): return self.name - + def __repr__(self): return "Person(name=%r)" % self.name - + def format_full(self): return "\t".join([str(x) for x in [self] + list(self.addresses)]) - + # Caching options. A set of three RelationshipCache options # which can be applied to Query(), causing the "lazy load" # of these attributes to be loaded from cache. diff --git a/examples/custom_attributes/custom_management.py b/examples/custom_attributes/custom_management.py index 2e2689140..644d512cf 100644 --- a/examples/custom_attributes/custom_management.py +++ b/examples/custom_attributes/custom_management.py @@ -87,7 +87,7 @@ class MyClass(object): class MyCollectionAdapter(object): """An wholly alternative instrumentation implementation.""" - + def __init__(self, key, state, collection): self.key = key self.state = state diff --git a/examples/custom_attributes/listen_for_events.py b/examples/custom_attributes/listen_for_events.py index 2899f7fb1..80a33c0d8 100644 --- a/examples/custom_attributes/listen_for_events.py +++ b/examples/custom_attributes/listen_for_events.py @@ -28,21 +28,21 @@ if __name__ == '__main__': from sqlalchemy.ext.declarative import declarative_base class Base(object): - + def receive_change_event(self, verb, key, value, oldvalue): s = "Value '%s' %s on attribute '%s', " % (value, verb, key) if oldvalue: s += "which replaced the value '%s', " % oldvalue s += "on object %s" % self print s - + Base = declarative_base(cls=Base) event.listen(Base, 'attribute_instrument', configure_listener) class MyMappedClass(Base): __tablename__ = "mytable" - + id = Column(Integer, primary_key=True) data = Column(String(50)) related_id = Column(Integer, ForeignKey("related.id")) @@ -50,7 +50,7 @@ if __name__ == '__main__': def __str__(self): return "MyMappedClass(data=%r)" % self.data - + class Related(Base): __tablename__ = "related" @@ -59,12 +59,12 @@ if __name__ == '__main__': def __str__(self): return "Related(data=%r)" % self.data - + # classes are instrumented. Demonstrate the events ! - + m1 = MyMappedClass(data='m1', related=Related(data='r1')) m1.data = 'm1mod' m1.related.mapped.append(MyMappedClass(data='m2')) del m1.data - - + + diff --git a/examples/dynamic_dict/dynamic_dict.py b/examples/dynamic_dict/dynamic_dict.py index 8d15fce0b..725d65619 100644 --- a/examples/dynamic_dict/dynamic_dict.py +++ b/examples/dynamic_dict/dynamic_dict.py @@ -4,15 +4,15 @@ class ProxyDict(object): self.collection_name = collection_name
self.childclass = childclass
self.keyname = keyname
-
+
@property
def collection(self):
return getattr(self.parent, self.collection_name)
-
+
def keys(self):
descriptor = getattr(self.childclass, self.keyname)
return [x[0] for x in self.collection.values(descriptor)]
-
+
def __getitem__(self, key):
x = self.collection.filter_by(**{self.keyname:key}).first()
if x:
@@ -40,11 +40,11 @@ class Parent(Base): id = Column(Integer, primary_key=True)
name = Column(String(50))
_collection = relationship("Child", lazy="dynamic", cascade="all, delete-orphan")
-
+
@property
def child_map(self):
return ProxyDict(self, '_collection', Child, 'key')
-
+
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
@@ -53,7 +53,7 @@ class Child(Base): def __repr__(self):
return "Child(key=%r)" % self.key
-
+
Base.metadata.create_all()
sess = sessionmaker()()
diff --git a/examples/elementtree/__init__.py b/examples/elementtree/__init__.py index 33805c0cb..8d47f4ace 100644 --- a/examples/elementtree/__init__.py +++ b/examples/elementtree/__init__.py @@ -21,15 +21,15 @@ In order of complexity: the load in a non-recursive fashion and is much more efficient. E.g.:: - + # parse an XML file and persist in the database doc = ElementTree.parse("test.xml") session.add(Document(file, doc)) session.commit() - + # locate documents with a certain path/attribute structure for document in find_document('/somefile/header/field2[@attr=foo]'): # dump the XML print document - + """
\ No newline at end of file diff --git a/examples/elementtree/optimized_al.py b/examples/elementtree/optimized_al.py index d6110a132..102f6c373 100644 --- a/examples/elementtree/optimized_al.py +++ b/examples/elementtree/optimized_al.py @@ -18,8 +18,8 @@ e = create_engine('sqlite://', echo=True) meta = MetaData() ####################### PART II - Table Metadata ############################# - -# stores a top level record of an XML document. + +# stores a top level record of an XML document. documents = Table('documents', meta, Column('document_id', Integer, primary_key=True), Column('filename', String(30), unique=True), @@ -48,12 +48,12 @@ meta.create_all(e) ########################### PART III - Model ################################# # our document class. contains a string name, -# and the ElementTree root element. +# and the ElementTree root element. class Document(object): def __init__(self, name, element): self.filename = name self.element = element - + def __str__(self): buf = StringIO.StringIO() self.element.write(buf) @@ -101,10 +101,10 @@ class ElementTreeMarshal(object): def __get__(self, document, owner): if document is None: return self - + if hasattr(document, '_element'): return document._element - + nodes = {} root = None for node in document._nodes: @@ -120,10 +120,10 @@ class ElementTreeMarshal(object): elem.attrib[attr.name] = attr.value elem.text = node.text elem.tail = node.tail - + document._element = ElementTree.ElementTree(root) return document._element - + def __set__(self, document, element): def traverse(node): n = _Node() @@ -137,7 +137,7 @@ class ElementTreeMarshal(object): traverse(element.getroot()) document._element = element - + def __delete__(self, document): del document._element document._nodes = [] diff --git a/examples/elementtree/pickle.py b/examples/elementtree/pickle.py index 5e53e6798..28bee4672 100644 --- a/examples/elementtree/pickle.py +++ b/examples/elementtree/pickle.py @@ -22,7 +22,7 @@ meta = MetaData() def are_elements_equal(x, y): return x == y -# stores a top level record of an XML document. +# stores a top level record of an XML document. # the "element" column will store the ElementTree document as a BLOB. documents = Table('documents', meta, Column('document_id', Integer, primary_key=True), @@ -33,7 +33,7 @@ documents = Table('documents', meta, meta.create_all(e) # our document class. contains a string name, -# and the ElementTree root element. +# and the ElementTree root element. class Document(object): def __init__(self, name, element): self.filename = name @@ -47,7 +47,7 @@ mapper(Document, documents) # get ElementTree document filename = os.path.join(os.path.dirname(__file__), "test.xml") doc = ElementTree.parse(filename) - + # save to DB session = Session(e) session.add(Document("test.xml", doc)) diff --git a/examples/graphs/directed_graph.py b/examples/graphs/directed_graph.py index 6052c68bc..3ba602f00 100644 --- a/examples/graphs/directed_graph.py +++ b/examples/graphs/directed_graph.py @@ -9,20 +9,20 @@ Base = declarative_base() class Node(Base): __tablename__ = 'node' - + node_id = Column(Integer, primary_key=True) def __init__(self, id): self.node_id = id - + def add_neighbors(self, *nodes): for node in nodes: Edge(self, node) return self - + def higher_neighbors(self): return [x.higher_node for x in self.lower_edges] - + def lower_neighbors(self): return [x.lower_node for x in self.higher_edges] @@ -32,7 +32,7 @@ class Edge(Base): lower_id = Column(Integer, ForeignKey('node.node_id'), primary_key=True) - + higher_id = Column(Integer, ForeignKey('node.node_id'), primary_key=True) @@ -43,7 +43,7 @@ class Edge(Base): higher_node = relationship(Node, primaryjoin=higher_id==Node.node_id, backref='higher_edges') - + # here we have lower.node_id <= higher.node_id def __init__(self, n1, n2): if n1.node_id < n2.node_id: @@ -62,7 +62,7 @@ session = sessionmaker(engine)() # n1 -> n2 -> n5 # -> n7 # -> n3 -> n6 - + n1 = Node(1) n2 = Node(2) n3 = Node(3) diff --git a/examples/inheritance/concrete.py b/examples/inheritance/concrete.py index eb832a55d..84fc79cd5 100644 --- a/examples/inheritance/concrete.py +++ b/examples/inheritance/concrete.py @@ -25,7 +25,7 @@ class Employee(object): self.name = name def __repr__(self): return self.__class__.__name__ + " " + self.name - + class Manager(Employee): def __init__(self, name, manager_data): self.name = name @@ -33,7 +33,7 @@ class Manager(Employee): def __repr__(self): return self.__class__.__name__ + " " + \ self.name + " " + self.manager_data - + class Engineer(Employee): def __init__(self, name, engineer_info): self.name = name diff --git a/examples/inheritance/polymorph.py b/examples/inheritance/polymorph.py index 87c2de10e..316671bed 100644 --- a/examples/inheritance/polymorph.py +++ b/examples/inheritance/polymorph.py @@ -20,7 +20,7 @@ people = Table('people', metadata, Column('company_id', Integer, ForeignKey('companies.company_id')), Column('name', String(50)), Column('type', String(30))) - + engineers = Table('engineers', metadata, Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), @@ -28,14 +28,14 @@ engineers = Table('engineers', metadata, Column('engineer_name', String(50)), Column('primary_language', String(50)), ) - + managers = Table('managers', metadata, Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), Column('status', String(30)), Column('manager_name', String(50)) ) - + # create our classes. The Engineer and Manager classes extend from Person. class Person(object): def __init__(self, **kwargs): diff --git a/examples/large_collection/large_collection.py b/examples/large_collection/large_collection.py index b8ade43ba..a251861d6 100644 --- a/examples/large_collection/large_collection.py +++ b/examples/large_collection/large_collection.py @@ -10,18 +10,18 @@ org_table = Table('organizations', meta, Column('org_id', Integer, primary_key=True), Column('org_name', String(50), nullable=False, key='name'), mysql_engine='InnoDB') - + member_table = Table('members', meta, Column('member_id', Integer, primary_key=True), Column('member_name', String(50), nullable=False, key='name'), Column('org_id', Integer, ForeignKey('organizations.org_id', ondelete="CASCADE")), mysql_engine='InnoDB') - - + + class Organization(object): def __init__(self, name): self.name = name - + class Member(object): def __init__(self, name): self.name = name @@ -31,11 +31,11 @@ mapper(Organization, org_table, properties = { # Organization.members will be a Query object - no loading # of the entire collection occurs unless requested lazy="dynamic", - + # Member objects "belong" to their parent, are deleted when # removed from the collection cascade="all, delete-orphan", - + # "delete, delete-orphan" cascade does not load in objects on delete, # allows ON DELETE CASCADE to handle it. # this only works with a database that supports ON DELETE CASCADE - @@ -49,7 +49,7 @@ mapper(Member, member_table) if __name__ == '__main__': engine = create_engine("mysql://scott:tiger@localhost/test", echo=True) meta.create_all(engine) - + # expire_on_commit=False means the session contents # will not get invalidated after commit. sess = sessionmaker(engine, expire_on_commit=False)() @@ -86,9 +86,9 @@ if __name__ == '__main__': sess.delete(org) print "-------------------------\nflush three - delete org, delete members in one statement\n" sess.commit() - + print "-------------------------\nno Member rows should remain:\n" print sess.query(Member).count() - + print "------------------------\ndone. dropping tables." meta.drop_all(engine)
\ No newline at end of file diff --git a/examples/nested_sets/nested_sets.py b/examples/nested_sets/nested_sets.py index fbb481759..55d734d4e 100644 --- a/examples/nested_sets/nested_sets.py +++ b/examples/nested_sets/nested_sets.py @@ -22,7 +22,7 @@ class NestedSetExtension(MapperExtension): right_most_sibling = connection.scalar( select([personnel.c.rgt]).where(personnel.c.emp==instance.parent.emp) ) - + connection.execute( personnel.update(personnel.c.rgt>=right_most_sibling).values( lft = case( @@ -41,21 +41,21 @@ class NestedSetExtension(MapperExtension): # before_update() would be needed to support moving of nodes # after_delete() would be needed to support removal of nodes. # [ticket:1172] needs to be implemented for deletion to work as well. - + class Employee(Base): __tablename__ = 'personnel' __mapper_args__ = { 'extension':NestedSetExtension(), 'batch':False # allows extension to fire for each instance before going to the next. } - + parent = None - + emp = Column(String, primary_key=True) - + left = Column("lft", Integer, nullable=False) right = Column("rgt", Integer, nullable=False) - + def __repr__(self): return "Employee(%s, %d, %d)" % (self.emp, self.left, self.right) @@ -100,4 +100,4 @@ for indentation, employee in session.query(func.count(Employee.emp).label('inden group_by(ealias.emp).\ order_by(ealias.left): print " " * indentation + str(employee) - + diff --git a/examples/poly_assoc/poly_assoc_generic.py b/examples/poly_assoc/poly_assoc_generic.py index aaa182df0..b674e1eb8 100644 --- a/examples/poly_assoc/poly_assoc_generic.py +++ b/examples/poly_assoc/poly_assoc_generic.py @@ -53,12 +53,12 @@ def association(cls, table): setattr(self, attr_name, GenericAssoc(table.name)) getattr(self, attr_name).targets = [value] setattr(cls, name, property(get, set)) - + @property def member(self): return getattr(self.association, '_backref_%s' % self.association.type) - + setattr(cls, 'member', member) mapper(GenericAssoc, association_table, properties={ diff --git a/examples/postgis/__init__.py b/examples/postgis/__init__.py index 1a351f7ae..3eb4ed3bc 100644 --- a/examples/postgis/__init__.py +++ b/examples/postgis/__init__.py @@ -7,20 +7,20 @@ The example illustrates: * a DDL extension which allows CREATE/DROP to work in conjunction with AddGeometryColumn/DropGeometryColumn - + * a Geometry type, as well as a few subtypes, which convert result row values to a GIS-aware object, and also integrates with the DDL extension. * a GIS-aware object which stores a raw geometry value and provides a factory for functions such as AsText(). - + * an ORM comparator which can override standard column methods on mapped objects to produce GIS operators. - + * an attribute event listener that intercepts strings and converts to GeomFromText(). - + * a standalone operator example. The implementation is limited to only public, well known diff --git a/examples/postgis/postgis.py b/examples/postgis/postgis.py index d3f728293..68f5ecae9 100644 --- a/examples/postgis/postgis.py +++ b/examples/postgis/postgis.py @@ -24,18 +24,18 @@ class GisElement(object): class PersistentGisElement(GisElement): """Represents a Geometry value as loaded from the database.""" - + def __init__(self, desc): self.desc = desc class TextualGisElement(GisElement, expression.Function): """Represents a Geometry value as expressed within application code; i.e. in wkt format. - + Extends expression.Function so that the value is interpreted as GeomFromText(value) in a SQL expression context. - + """ - + def __init__(self, desc, srid=-1): assert isinstance(desc, basestring) self.desc = desc @@ -46,17 +46,17 @@ class TextualGisElement(GisElement, expression.Function): class Geometry(TypeEngine): """Base PostGIS Geometry column type. - + Converts bind/result values to/from a PersistentGisElement. - + """ - + name = 'GEOMETRY' - + def __init__(self, dimension=None, srid=-1): self.dimension = dimension self.srid = srid - + def bind_processor(self, dialect): def process(value): if value is not None: @@ -64,7 +64,7 @@ class Geometry(TypeEngine): else: return value return process - + def result_processor(self, dialect, coltype): def process(value): if value is not None: @@ -78,10 +78,10 @@ class Geometry(TypeEngine): class Point(Geometry): name = 'POINT' - + class Curve(Geometry): name = 'CURVE' - + class LineString(Curve): name = 'LINESTRING' @@ -93,36 +93,36 @@ class LineString(Curve): class GISDDL(object): """A DDL extension which integrates SQLAlchemy table create/drop methods with PostGis' AddGeometryColumn/DropGeometryColumn functions. - + Usage:: - + sometable = Table('sometable', metadata, ...) - + GISDDL(sometable) sometable.create() - + """ - + def __init__(self, table): for event in ('before-create', 'after-create', 'before-drop', 'after-drop'): table.ddl_listeners[event].append(self) self._stack = [] - + def __call__(self, event, table, bind): if event in ('before-create', 'before-drop'): regular_cols = [c for c in table.c if not isinstance(c.type, Geometry)] gis_cols = set(table.c).difference(regular_cols) self._stack.append(table.c) table._columns = expression.ColumnCollection(*regular_cols) - + if event == 'before-drop': for c in gis_cols: bind.execute(select([func.DropGeometryColumn('public', table.name, c.name)], autocommit=True)) - + elif event == 'after-create': table._columns = self._stack.pop() - + for c in table.c: if isinstance(c.type, Geometry): bind.execute(select([func.AddGeometryColumn(table.name, c.name, c.type.srid, c.type.name, c.type.dimension)], autocommit=True)) @@ -133,7 +133,7 @@ class GISDDL(object): def _to_postgis(value): """Interpret a value as a GIS-compatible construct.""" - + if hasattr(value, '__clause_element__'): return value.__clause_element__() elif isinstance(value, (expression.ClauseElement, GisElement)): @@ -149,18 +149,18 @@ def _to_postgis(value): class GisAttribute(AttributeExtension): """Intercepts 'set' events on a mapped instance attribute and converts the incoming value to a GIS expression. - + """ - + def set(self, state, value, oldvalue, initiator): return _to_postgis(value) - + class GisComparator(ColumnProperty.ColumnComparator): """Intercepts standard Column operators on mapped class attributes and overrides their behavior. - + """ - + # override the __eq__() operator def __eq__(self, other): return self.__clause_element__().op('~=')(_to_postgis(other)) @@ -168,19 +168,19 @@ class GisComparator(ColumnProperty.ColumnComparator): # add a custom operator def intersects(self, other): return self.__clause_element__().op('&&')(_to_postgis(other)) - + # any number of GIS operators can be overridden/added here # using the techniques above. - + def GISColumn(*args, **kw): """Define a declarative column property with GIS behavior. - + This just produces orm.column_property() with the appropriate extension and comparator_factory arguments. The given arguments are passed through to Column. The declarative module extracts the Column for inclusion in the mapped table. - + """ return column_property( Column(*args, **kw), @@ -201,21 +201,21 @@ if __name__ == '__main__': class Road(Base): __tablename__ = 'roads' - + road_id = Column(Integer, primary_key=True) road_name = Column(String) road_geom = GISColumn(Geometry(2)) - + # enable the DDL extension, which allows CREATE/DROP operations # to work correctly. This is not needed if working with externally - # defined tables. + # defined tables. GISDDL(Road.__table__) metadata.drop_all() metadata.create_all() session = sessionmaker(bind=engine)() - + # Add objects. We can use strings... session.add_all([ Road(road_name='Jeff Rd', road_geom='LINESTRING(191232 243118,191108 243242)'), @@ -224,30 +224,30 @@ if __name__ == '__main__': Road(road_name='Graeme Ave', road_geom='LINESTRING(189412 252431,189631 259122)'), Road(road_name='Phil Tce', road_geom='LINESTRING(190131 224148,190871 228134)'), ]) - + # or use an explicit TextualGisElement (similar to saying func.GeomFromText()) r = Road(road_name='Dave Cres', road_geom=TextualGisElement('LINESTRING(198231 263418,198213 268322)', -1)) session.add(r) - + # pre flush, the TextualGisElement represents the string we sent. assert str(r.road_geom) == 'LINESTRING(198231 263418,198213 268322)' assert session.scalar(r.road_geom.wkt) == 'LINESTRING(198231 263418,198213 268322)' - + session.commit() # after flush and/or commit, all the TextualGisElements become PersistentGisElements. assert str(r.road_geom) == "01020000000200000000000000B832084100000000E813104100000000283208410000000088601041" - + r1 = session.query(Road).filter(Road.road_name=='Graeme Ave').one() - + # illustrate the overridden __eq__() operator. - + # strings come in as TextualGisElements r2 = session.query(Road).filter(Road.road_geom == 'LINESTRING(189412 252431,189631 259122)').one() - + # PersistentGisElements work directly r3 = session.query(Road).filter(Road.road_geom == r1.road_geom).one() - + assert r1 is r2 is r3 # illustrate the "intersects" operator @@ -256,7 +256,7 @@ if __name__ == '__main__': # illustrate usage of the "wkt" accessor. this requires a DB # execution to call the AsText() function so we keep this explicit. assert session.scalar(r1.road_geom.wkt) == 'LINESTRING(189412 252431,189631 259122)' - + session.rollback() - + metadata.drop_all() diff --git a/examples/sharding/attribute_shard.py b/examples/sharding/attribute_shard.py index 6b4813dd3..9f1157c32 100644 --- a/examples/sharding/attribute_shard.py +++ b/examples/sharding/attribute_shard.py @@ -74,12 +74,12 @@ weather_reports = Table("weather_reports", meta, for db in (db1, db2, db3, db4): meta.drop_all(db) meta.create_all(db) - + # establish initial "id" in db1 db1.execute(ids.insert(), nextid=1) -# step 5. define sharding functions. +# step 5. define sharding functions. # we'll use a straight mapping of a particular set of "country" # attributes to shard id. @@ -92,12 +92,12 @@ shard_lookup = { def shard_chooser(mapper, instance, clause=None): """shard chooser. - + looks at the given instance and returns a shard id note that we need to define conditions for the WeatherLocation class, as well as our secondary Report class which will point back to its WeatherLocation via its 'location' attribute. - + """ if isinstance(instance, WeatherLocation): return shard_lookup[instance.continent] @@ -105,24 +105,24 @@ def shard_chooser(mapper, instance, clause=None): return shard_chooser(mapper, instance.location) def id_chooser(query, ident): - """id chooser. - + """id chooser. + given a primary key, returns a list of shards to search. here, we don't have any particular information from a pk so we just return all shard ids. often, youd want to do some kind of round-robin strategy here so that requests are evenly distributed among DBs. - + """ return ['north_america', 'asia', 'europe', 'south_america'] def query_chooser(query): """query chooser. - + this also returns a list of shard ids, which can just be all of them. but here we'll search into the Query in order to try to narrow down the list of shards to query. - + """ ids = [] @@ -140,7 +140,7 @@ def query_chooser(query): ids.append(shard_lookup[value]) elif operator == operators.in_op: ids.extend(shard_lookup[v] for v in value) - + if len(ids) == 0: return ['north_america', 'asia', 'europe', 'south_america'] else: @@ -148,12 +148,12 @@ def query_chooser(query): def _get_query_comparisons(query): """Search an orm.Query object for binary expressions. - + Returns expressions which match a Column against one or more literal values as a list of tuples of the form (column, operator, values). "values" is a single value or tuple of values depending on the operator. - + """ binds = {} clauses = set() @@ -216,7 +216,7 @@ create_session.configure( query_chooser=query_chooser ) -# step 6. mapped classes. +# step 6. mapped classes. class WeatherLocation(object): def __init__(self, continent, city): self.continent = continent @@ -231,7 +231,7 @@ mapper(WeatherLocation, weather_locations, properties={ 'reports':relationship(Report, backref='location') }) -mapper(Report, weather_reports) +mapper(Report, weather_reports) # save and load objects! diff --git a/examples/versioning/__init__.py b/examples/versioning/__init__.py index f2f8832cf..72edcca53 100644 --- a/examples/versioning/__init__.py +++ b/examples/versioning/__init__.py @@ -19,13 +19,13 @@ A fragment of example usage, using declarative:: class SomeClass(Base): __tablename__ = 'sometable' - + id = Column(Integer, primary_key=True) name = Column(String(50)) - + def __eq__(self, other): assert type(other) is SomeClass and other.id == self.id - + sess = Session() sc = SomeClass(name='sc1') sess.add(sc) diff --git a/examples/versioning/history_meta.py b/examples/versioning/history_meta.py index fa95733e2..2983a33e2 100644 --- a/examples/versioning/history_meta.py +++ b/examples/versioning/history_meta.py @@ -19,10 +19,10 @@ def _history_mapper(local_mapper): # of the info is always loaded (currently sets it on all attributes) for prop in local_mapper.iterate_properties: getattr(local_mapper.class_, prop.key).impl.active_history = True - + super_mapper = local_mapper.inherits super_history_mapper = getattr(cls, '__history_mapper__', None) - + polymorphic_on = None super_fks = [] if not super_mapper or local_mapper.local_table is not super_mapper.local_table: @@ -30,7 +30,7 @@ def _history_mapper(local_mapper): for column in local_mapper.local_table.c: if column.name == 'version': continue - + col = column.copy() col.unique = False @@ -38,16 +38,16 @@ def _history_mapper(local_mapper): super_fks.append((col.key, list(super_history_mapper.base_mapper.local_table.primary_key)[0])) cols.append(col) - + if column is local_mapper.polymorphic_on: polymorphic_on = col - + if super_mapper: super_fks.append(('version', super_history_mapper.base_mapper.local_table.c.version)) cols.append(Column('version', Integer, primary_key=True)) else: cols.append(Column('version', Integer, primary_key=True)) - + if super_fks: cols.append(ForeignKeyConstraint(*zip(*super_fks))) @@ -62,13 +62,13 @@ def _history_mapper(local_mapper): col = column.copy() super_history_mapper.local_table.append_column(col) table = None - + if super_history_mapper: bases = (super_history_mapper.class_,) else: bases = local_mapper.base_mapper.class_.__bases__ versioned_cls = type.__new__(type, "%sHistory" % cls.__name__, bases, {}) - + m = mapper( versioned_cls, table, @@ -77,11 +77,11 @@ def _history_mapper(local_mapper): polymorphic_identity=local_mapper.polymorphic_identity ) cls.__history_mapper__ = m - + if not super_history_mapper: cls.version = Column('version', Integer, default=1, nullable=False) - - + + class VersionedMeta(DeclarativeMeta): def __init__(cls, classname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) @@ -102,21 +102,21 @@ def create_version(obj, session, deleted = False): obj_mapper = object_mapper(obj) history_mapper = obj.__history_mapper__ history_cls = history_mapper.class_ - + obj_state = attributes.instance_state(obj) - + attr = {} obj_changed = False - + for om, hm in zip(obj_mapper.iterate_to_root(), history_mapper.iterate_to_root()): if hm.single: continue - + for hist_col in hm.local_table.c: if hist_col.key == 'version': continue - + obj_col = om.local_table.c[hist_col.key] # get the value of the @@ -131,7 +131,7 @@ def create_version(obj, session, deleted = False): # the "unmapped" status of the subclass column on the # base class is a feature of the declarative module as of sqla 0.5.2. continue - + # expired object attributes and also deferred cols might not be in the # dict. force it to load no matter what by using getattr(). if prop.key not in obj_state.dict: @@ -148,7 +148,7 @@ def create_version(obj, session, deleted = False): # if the attribute had no value. attr[hist_col.key] = a[0] obj_changed = True - + if not obj_changed: # not changed, but we have relationships. OK # check those too @@ -157,8 +157,8 @@ def create_version(obj, session, deleted = False): attributes.get_history(obj, prop.key).has_changes(): obj_changed = True break - - if not obj_changed and not deleted: + + if not obj_changed and not deleted: return attr['version'] = obj.version @@ -167,7 +167,7 @@ def create_version(obj, session, deleted = False): setattr(hist, key, value) session.add(hist) obj.version += 1 - + class VersionedListener(SessionExtension): def before_flush(self, session, flush_context, instances): for obj in versioned_objects(session.dirty): diff --git a/examples/versioning/test_versioning.py b/examples/versioning/test_versioning.py index c9cb605cd..83d769e1e 100644 --- a/examples/versioning/test_versioning.py +++ b/examples/versioning/test_versioning.py @@ -8,7 +8,7 @@ from test.lib.entities import ComparableEntity def setup(): global engine engine = create_engine('sqlite://', echo=True) - + class TestVersioning(TestBase): def setup(self): global Base, Session, Versioned @@ -17,34 +17,34 @@ class TestVersioning(TestBase): __metaclass__ = VersionedMeta _decl_class_registry = Base._decl_class_registry Session = sessionmaker(extension=VersionedListener()) - + def teardown(self): clear_mappers() Base.metadata.drop_all() - + def create_tables(self): Base.metadata.create_all() - + def test_plain(self): class SomeClass(Versioned, Base, ComparableEntity): __tablename__ = 'sometable' - + id = Column(Integer, primary_key=True) name = Column(String(50)) - + self.create_tables() sess = Session() sc = SomeClass(name='sc1') sess.add(sc) sess.commit() - + sc.name = 'sc1modified' sess.commit() - + assert sc.version == 2 - + SomeClassHistory = SomeClass.__history_mapper__.class_ - + eq_( sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1')] @@ -61,7 +61,7 @@ class TestVersioning(TestBase): ) assert sc.version == 3 - + sess.commit() sc.name = 'temp' @@ -76,7 +76,7 @@ class TestVersioning(TestBase): SomeClassHistory(version=2, name='sc1modified') ] ) - + sess.delete(sc) sess.commit() @@ -92,41 +92,41 @@ class TestVersioning(TestBase): def test_from_null(self): class SomeClass(Versioned, Base, ComparableEntity): __tablename__ = 'sometable' - + id = Column(Integer, primary_key=True) name = Column(String(50)) - + self.create_tables() sess = Session() sc = SomeClass() sess.add(sc) sess.commit() - + sc.name = 'sc1' sess.commit() - + assert sc.version == 2 def test_deferred(self): """test versioning of unloaded, deferred columns.""" - + class SomeClass(Versioned, Base, ComparableEntity): __tablename__ = 'sometable' id = Column(Integer, primary_key=True) name = Column(String(50)) data = deferred(Column(String(25))) - + self.create_tables() sess = Session() sc = SomeClass(name='sc1', data='somedata') sess.add(sc) sess.commit() sess.close() - + sc = sess.query(SomeClass).first() assert 'data' not in sc.__dict__ - + sc.name = 'sc1modified' sess.commit() @@ -138,8 +138,8 @@ class TestVersioning(TestBase): sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1', data='somedata')] ) - - + + def test_joined_inheritance(self): class BaseClass(Versioned, Base, ComparableEntity): __tablename__ = 'basetable' @@ -147,9 +147,9 @@ class TestVersioning(TestBase): id = Column(Integer, primary_key=True) name = Column(String(50)) type = Column(String(20)) - + __mapper_args__ = {'polymorphic_on':type, 'polymorphic_identity':'base'} - + class SubClassSeparatePk(BaseClass): __tablename__ = 'subtable1' @@ -175,7 +175,7 @@ class TestVersioning(TestBase): same1 = SubClassSamePk(name='same1', subdata2='same1subdata') sess.add_all([sep1, base1, same1]) sess.commit() - + base1.name = 'base1mod' same1.subdata2 = 'same1subdatamod' sep1.name ='sep1mod' @@ -189,10 +189,10 @@ class TestVersioning(TestBase): [ SubClassSeparatePkHistory(id=1, name=u'sep1', type=u'sep', version=1), BaseClassHistory(id=2, name=u'base1', type=u'base', version=1), - SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=1) + SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=1) ] ) - + same1.subdata2 = 'same1subdatamod2' eq_( @@ -201,7 +201,7 @@ class TestVersioning(TestBase): SubClassSeparatePkHistory(id=1, name=u'sep1', type=u'sep', version=1), BaseClassHistory(id=2, name=u'base1', type=u'base', version=1), SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=1), - SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=2) + SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=2) ] ) @@ -213,7 +213,7 @@ class TestVersioning(TestBase): BaseClassHistory(id=2, name=u'base1', type=u'base', version=1), BaseClassHistory(id=2, name=u'base1mod', type=u'base', version=2), SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=1), - SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=2) + SubClassSamePkHistory(id=3, name=u'same1', type=u'same', version=2) ] ) @@ -225,7 +225,7 @@ class TestVersioning(TestBase): name = Column(String(50)) type = Column(String(50)) __mapper_args__ = {'polymorphic_on':type, 'polymorphic_identity':'base'} - + class SubClass(BaseClass): subname = Column(String(50)) @@ -236,21 +236,21 @@ class TestVersioning(TestBase): b1 = BaseClass(name='b1') sc = SubClass(name='s1', subname='sc1') - + sess.add_all([b1, sc]) - + sess.commit() - + b1.name='b1modified' BaseClassHistory = BaseClass.__history_mapper__.class_ SubClassHistory = SubClass.__history_mapper__.class_ - + eq_( sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), [BaseClassHistory(id=1, name=u'b1', type=u'base', version=1)] ) - + sc.name ='s1modified' b1.name='b1modified2' @@ -262,48 +262,48 @@ class TestVersioning(TestBase): SubClassHistory(id=2, name=u's1', type=u'sub', version=1) ] ) - + def test_unique(self): class SomeClass(Versioned, Base, ComparableEntity): __tablename__ = 'sometable' - + id = Column(Integer, primary_key=True) name = Column(String(50), unique=True) data = Column(String(50)) - + self.create_tables() sess = Session() sc = SomeClass(name='sc1', data='sc1') sess.add(sc) sess.commit() - + sc.data = 'sc1modified' sess.commit() - + assert sc.version == 2 - + sc.data = 'sc1modified2' sess.commit() - + assert sc.version == 3 def test_relationship(self): class SomeRelated(Base, ComparableEntity): __tablename__ = 'somerelated' - + id = Column(Integer, primary_key=True) class SomeClass(Versioned, Base, ComparableEntity): __tablename__ = 'sometable' - + id = Column(Integer, primary_key=True) name = Column(String(50)) related_id = Column(Integer, ForeignKey('somerelated.id')) related = relationship("SomeRelated") - + SomeClassHistory = SomeClass.__history_mapper__.class_ - + self.create_tables() sess = Session() sc = SomeClass(name='sc1') @@ -311,13 +311,13 @@ class TestVersioning(TestBase): sess.commit() assert sc.version == 1 - + sr1 = SomeRelated() sc.related = sr1 sess.commit() - + assert sc.version == 2 - + eq_( sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1', related_id=None)] @@ -334,4 +334,4 @@ class TestVersioning(TestBase): ) assert sc.version == 3 - + diff --git a/examples/vertical/__init__.py b/examples/vertical/__init__.py index 61ba2228b..b4ce291f2 100644 --- a/examples/vertical/__init__.py +++ b/examples/vertical/__init__.py @@ -1,7 +1,7 @@ """ Illustrates "vertical table" mappings. -A "vertical table" refers to a technique where individual attributes of an object are stored as distinct rows in a table. +A "vertical table" refers to a technique where individual attributes of an object are stored as distinct rows in a table. The "vertical table" technique is used to persist objects which can have a varied set of attributes, at the expense of simple query control and brevity. It is commonly found in content/document management systems in order to represent user-created structures flexibly. Two variants on the approach are given. In the second, each row references a "datatype" which contains information about the type of information stored in the attribute, such as integer, string, or date. |
