root / elixir / trunk / elixir / __init__.py @ 185

Revision 185, 5.7 kB (checked in by cleverdevil, 6 years ago)

Patch from Mike Bayer:

The attached patch modifies Elixir's "Objectstore" implementation to work
for a SessionContext from versions 0.3 and 0.4, as well as the
0.4-onlyScopedSession object. Additionally, a new using_options() option
"session" is added, such that any user-defined SessionContext or
ScopedSession may be assigned to an Elixir class individually. So the
contextual "Session" which is to be used by Elixir can now be set at three
levels; globally using elixir.objectstore, per-module using module.session,
or per-class using using_options(session=somesess).


Three tests are added, two of which test using_options(session) against

both kinds of session, and a third which tests module-level usage of
ScopedSession.


At the very least, the new "Objectstore" class should be implemented which

establishes compatibility with ScopedSession which is featured in the Pylons
tutorial.


the patch also fixes a bug in versioned.py where an execute() statement
could be issued using unicode keywords, which is illegal for the **params
calling style.

Thanks, Mike!


Line 
1'''
2Elixir package
3
4A declarative layer on top of SQLAlchemy, which is intended to replace the
5ActiveMapper SQLAlchemy extension, and the TurboEntity project.  Elixir is a
6fairly thin wrapper around SQLAlchemy, which provides the ability to define
7model objects following the Active Record design pattern, and using a DSL
8syntax similar to that of the Ruby on Rails ActiveRecord system.
9
10Elixir does not intend to replace SQLAlchemy's core features, but instead
11focuses on providing a simpler syntax for defining model objects when you do
12not need the full expressiveness of SQLAlchemy's manual mapper definitions.
13
14For an example of how to use Elixir, please refer to the examples directory
15and the unit tests. The examples directory includes a TurboGears application
16with full identity support called 'videostore'.
17'''
18
19import sqlalchemy
20
21from sqlalchemy.ext.sessioncontext import SessionContext
22from sqlalchemy.types import *
23
24from elixir.options import using_options, using_table_options, \
25                           using_mapper_options, options_defaults
26from elixir.entity import Entity, EntityMeta, EntityDescriptor, Objectstore
27from elixir.fields import has_field, with_fields, Field
28from elixir.relationships import belongs_to, has_one, has_many, \
29                                 has_and_belongs_to_many
30from elixir.properties import has_property
31from elixir.statements import Statement
32
33try:
34    set
35except NameError:
36    from sets import Set as set
37
38__version__ = '0.4.0'
39
40__all__ = ['Entity', 'EntityMeta', 'Field', 'has_field', 'with_fields',
41           'has_property', 
42           'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many',
43           'using_options', 'using_table_options', 'using_mapper_options',
44           'options_defaults', 'metadata', 'objectstore',
45           'create_all', 'drop_all', 'setup_all', 'cleanup_all'] + \
46          sqlalchemy.types.__all__
47
48__pudge_all__ = ['create_all', 'drop_all', 'setup_all', 'cleanup_all',
49                 'metadata', 'objectstore']
50
51# connect
52metadata = sqlalchemy.MetaData()
53
54try:
55    # this only happens when the threadlocal extension is used
56    objectstore = sqlalchemy.objectstore
57except AttributeError:
58    objectstore = Objectstore(SessionContext(sqlalchemy.orm.create_session))
59
60metadatas = set()
61
62
63def create_all(engine=None):
64    'Create all necessary tables for all declared entities'
65    for md in metadatas:
66        md.create_all(bind=engine)
67
68
69def drop_all(engine=None):
70    'Drop all tables for all declared entities'
71    for md in metadatas:
72        md.drop_all(bind=engine)
73
74_delayed_descriptors = list()
75
76def setup_all(create_tables=False):
77    '''Setup the table and mapper for all entities which have been delayed.
78
79    This is called automatically when your entity is first accessed, or ...
80    [TODO: complete this]
81    '''
82
83    if not _delayed_descriptors:
84        return
85
86#TODO: define all those operations as methods on the descriptor
87#    for method_name in ('setup_table', 'setup_mapper', 'setup_relkeys', ...):
88#        for desc in _delayed_descriptors:
89#            method = getattr(desc, method_name)
90#            method()
91    try:
92        for desc in _delayed_descriptors:
93            entity = desc.entity
94            entity._ready = False
95            del sqlalchemy.orm.mapper_registry[entity._class_key]
96
97            md = desc.metadata
98            # the table could have already been removed (namely in a single
99            # table inheritance scenario)
100            md.tables.pop(entity._table_key, None)
101
102            # restore original table iterator if not done already
103            if hasattr(md.table_iterator, '_non_elixir_patched_iterator'):
104                md.table_iterator = \
105                    md.table_iterator._non_elixir_patched_iterator
106
107        # Make sure autoloaded tables are available so that we can setup
108        # foreign keys to their columns
109        for desc in _delayed_descriptors:
110            if desc.autoload:
111                desc.setup_table()
112
113        for desc in _delayed_descriptors:
114            desc.create_pk_cols()
115
116        # Create other columns from belongs_to relationships.
117        for desc in _delayed_descriptors:
118            for rel in desc.relationships.itervalues():
119                rel.create_keys(False)
120
121        for desc in _delayed_descriptors:
122            if not desc.autoload:
123                desc.setup_table()
124
125        for desc in _delayed_descriptors:
126            Statement.process(desc.entity, 'before_table')
127
128        for desc in _delayed_descriptors:
129            for rel in desc.relationships.itervalues():
130                rel.create_tables()
131
132        for desc in _delayed_descriptors:
133            Statement.process(desc.entity, 'after_table')
134
135        for desc in _delayed_descriptors:
136            desc.setup_events()
137       
138        for desc in _delayed_descriptors:
139            Statement.process(desc.entity, 'before_mapper')
140
141        for desc in _delayed_descriptors:
142            desc.setup_mapper()
143
144        for desc in _delayed_descriptors:
145            Statement.process(desc.entity, 'after_mapper')
146
147        for desc in _delayed_descriptors:
148            for rel in desc.relationships.itervalues():
149                rel.create_properties()
150
151        for desc in _delayed_descriptors:
152            Statement.process(desc.entity, 'finalize')
153
154    finally:
155        # make sure that even if we fail to initialize, we don't leave junk for
156        # others
157        del _delayed_descriptors[:]
158
159    # issue the "CREATE" SQL statements
160    if create_tables:
161        create_all()
162
163
164def cleanup_all(drop_tables=False):
165    '''Drop table and clear mapper for all entities, and clear all metadatas.
166    '''
167    if drop_tables:
168        drop_all()
169       
170    for md in metadatas:
171        md.clear()
172    metadatas.clear()
173
174    objectstore.clear()
175    sqlalchemy.orm.clear_mappers()
Note: See TracBrowser for help on using the browser.