diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-01-19 00:53:12 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-01-19 00:53:12 +0000 |
| commit | 40f8aadd582776524d3b98da1f577c2fc95619e7 (patch) | |
| tree | 753eec3802734f397953976824a252bb60829189 /examples/vertical | |
| parent | 56fe538cc7d81ce264fc6504feb1ead5e17d0f55 (diff) | |
| download | sqlalchemy-40f8aadd582776524d3b98da1f577c2fc95619e7.tar.gz | |
- mega example cleanup
- added READMEs to all examples in each __init__.py and added to sphinx documentation
- added versioning example
- removed vertical/vertical.py, the dictlikes are more straightforward
Diffstat (limited to 'examples/vertical')
| -rw-r--r-- | examples/vertical/__init__.py | 27 | ||||
| -rw-r--r-- | examples/vertical/dictlike-polymorphic.py | 2 | ||||
| -rw-r--r-- | examples/vertical/dictlike.py | 1 | ||||
| -rw-r--r-- | examples/vertical/vertical.py | 201 |
4 files changed, 28 insertions, 203 deletions
diff --git a/examples/vertical/__init__.py b/examples/vertical/__init__.py index e69de29bb..61ba2228b 100644 --- a/examples/vertical/__init__.py +++ b/examples/vertical/__init__.py @@ -0,0 +1,27 @@ +""" +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. +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. + + +Example:: + + shrew = Animal(u'shrew') + shrew[u'cuteness'] = 5 + shrew[u'weasel-like'] = False + shrew[u'poisonous'] = True + + session.add(shrew) + session.flush() + + q = (session.query(Animal). + filter(Animal.facts.any( + and_(AnimalFact.key == u'weasel-like', + AnimalFact.value == True)))) + print 'weasel-like animals', q.all() + + +"""
\ No newline at end of file diff --git a/examples/vertical/dictlike-polymorphic.py b/examples/vertical/dictlike-polymorphic.py index 6180fa303..a66576945 100644 --- a/examples/vertical/dictlike-polymorphic.py +++ b/examples/vertical/dictlike-polymorphic.py @@ -25,8 +25,6 @@ we'll use a Python @property to build a smart '.value' attribute that wraps up reading and writing those various '_value' columns and keeps the '.type' up to date. -Note: Something much like 'comparable_property' is slated for inclusion in a - future version of SQLAlchemy. """ from sqlalchemy.orm.interfaces import PropComparator diff --git a/examples/vertical/dictlike.py b/examples/vertical/dictlike.py index 1e1635ed7..683eda029 100644 --- a/examples/vertical/dictlike.py +++ b/examples/vertical/dictlike.py @@ -28,6 +28,7 @@ entity, and another related table holding key/value pairs:: Because the key/value pairs in a vertical scheme are not fixed in advance, accessing them like a Python dict can be very convenient. The example below can be used with many common vertical schemas as-is or with minor adaptations. + """ class VerticalProperty(object): diff --git a/examples/vertical/vertical.py b/examples/vertical/vertical.py deleted file mode 100644 index 4a8bf7787..000000000 --- a/examples/vertical/vertical.py +++ /dev/null @@ -1,201 +0,0 @@ -"""this example illustrates a "vertical table". an object is stored with individual attributes -represented in distinct database rows. This allows objects to be created with dynamically changing -fields that are all persisted in a normalized fashion.""" - -from sqlalchemy import (create_engine, MetaData, Table, Column, Integer, String, - ForeignKey, PickleType, DateTime, and_) -from sqlalchemy.orm import mapper, relation, sessionmaker, scoped_session -from sqlalchemy.orm.collections import mapped_collection -import datetime - -engine = create_engine('sqlite://', echo=False) -meta = MetaData(engine) - -Session = scoped_session(sessionmaker()) - -# represent Entity objects -entities = Table('entities', meta, - Column('entity_id', Integer, primary_key=True), - Column('title', String(100), nullable=False), - ) - -# represent named, typed fields -entity_fields = Table('entity_fields', meta, - Column('field_id', Integer, primary_key=True), - Column('name', String(40), nullable=False), - Column('datatype', String(30), nullable=False)) - -# associate a field row with an entity row, including a typed value -entity_values = Table('entity_values', meta, - Column('value_id', Integer, primary_key=True), - Column('field_id', Integer, ForeignKey('entity_fields.field_id'), nullable=False), - Column('entity_id', Integer, ForeignKey('entities.entity_id'), nullable=False), - Column('int_value', Integer), - Column('string_value', String(500)), - Column('binary_value', PickleType), - Column('datetime_value', DateTime)) - -meta.create_all() - -class Entity(object): - """a persistable dynamic object. - - Marshalls attributes into a dictionary which is - mapped to the database. - - """ - def __init__(self, **kwargs): - for k in kwargs: - setattr(self, k, kwargs[k]) - - def __getattr__(self, key): - """Proxy requests for attributes to the underlying _entities dictionary.""" - - if key[0] == '_': - return super(Entity, self).__getattr__(key) - try: - return self._entities[key].value - except KeyError: - raise AttributeError(key) - - def __setattr__(self, key, value): - """Proxy requests for attribute set operations to the underlying _entities dictionary.""" - - if key[0] == "_" or hasattr(Entity, key): - object.__setattr__(self, key, value) - return - - try: - ev = self._entities[key] - ev.value = value - except KeyError: - ev = _EntityValue(key, value) - self._entities[key] = ev - -class _EntityField(object): - """Represents a field of a particular name and datatype.""" - - def __init__(self, name, datatype): - self.name = name - self.datatype = datatype - -class _EntityValue(object): - """Represents an individual value.""" - - def __init__(self, key, value): - datatype = self._figure_datatype(value) - field = \ - Session.query(_EntityField).filter( - and_(_EntityField.name==key, _EntityField.datatype==datatype) - ).first() - - if not field: - field = _EntityField(key, datatype) - Session.add(field) - - self.field = field - setattr(self, self.field.datatype + "_value", value) - - def _figure_datatype(self, value): - typemap = { - int:'int', - str:'string', - datetime.datetime:'datetime', - } - for k in typemap: - if isinstance(value, k): - return typemap[k] - else: - return 'binary' - - def _get_value(self): - return getattr(self, self.field.datatype + "_value") - - def _set_value(self, value): - setattr(self, self.field.datatype + "_value", value) - value = property(_get_value, _set_value) - - def name(self): - return self.field.name - name = property(name) - - -# the mappers are a straightforward eager chain of -# Entity--(1->many)->EntityValue-(many->1)->EntityField -# notice that we are identifying each mapper to its connecting -# relation by just the class itself. -mapper(_EntityField, entity_fields) -mapper( - _EntityValue, entity_values, - properties = { - 'field' : relation(_EntityField, lazy=False, cascade='all') - } -) - -mapper(Entity, entities, properties = { - '_entities' : relation( - _EntityValue, - lazy=False, - cascade='all', - collection_class=mapped_collection(lambda entityvalue: entityvalue.field.name) - ) -}) - -session = Session() -entity1 = Entity( - title = 'this is the first entity', - name = 'this is the name', - price = 43, - data = ('hello', 'there') -) - -entity2 = Entity( - title = 'this is the second entity', - name = 'this is another name', - price = 50, - data = ('hoo', 'ha') -) - -session.add_all([entity1, entity2]) -session.commit() - -for entity in session.query(Entity): - print "Entity id %d:" % entity.entity_id, entity.title, entity.name, entity.price, entity.data - -# perform some changes, add a new Entity - -entity1.price = 90 -entity1.title = 'another new title' -entity2.data = {'oof':5,'lala':8} - -entity3 = Entity( - title = 'third entity', - name = 'new name', - price = '$1.95', # note we change 'price' to be a string. - # this creates a new _EntityField separate from the - # one used by integer 'price'. - data = 'some data' -) -session.add(entity3) - -session.commit() - -print "----------------" -for entity in session.query(Entity): - print "Entity id %d:" % entity.entity_id, entity.title, entity.name, entity.price, entity.data - -print "----------------" -# illustrate each _EntityField that's been created and list each Entity which uses it -for ent_id, name, datatype in session.query(_EntityField.field_id, _EntityField.name, _EntityField.datatype): - print name, datatype, "(Enitites:", ",".join([ - str(entid) for entid in session.query(Entity.entity_id).\ - join( - (_EntityValue, _EntityValue.entity_id==Entity.entity_id), - (_EntityField, _EntityField.field_id==_EntityValue.field_id) - ).filter(_EntityField.field_id==ent_id) - ]), ")" - -# delete all the Entity objects -for entity in session.query(Entity): - session.delete(entity) -session.commit() |
