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

Revision 196, 4.5 kB (checked in by ged, 7 years ago)
  • updated TODO file
  • (partially) changelogged what was done recently. Did I forget anything?
  • fixed single-table and multi-table inheritance which have relationships
    Concrete is still broken in this regard. I had to remove the "select_table"
    optimization of the multi-table polymorphic for that to work.
  • made each initialization step as a method on EntityDescriptor
  • reordered EntityDescriptor methods so that they are in the order they are
    called
  • added setup_entities method to be able to setup only some entities and not
    all of them together. This is not usable yet, as all entities are still
    appended to the _delayed_entities list whether you want it or not.
  • some misc cleanup
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',
46           'setup_all', 'cleanup_all'] + sqlalchemy.types.__all__
47
48__pudge_all__ = ['create_all', 'drop_all',
49                 'setup_all', 'cleanup_all',
50                 'metadata', 'objectstore']
51
52# connect
53metadata = sqlalchemy.MetaData()
54
55try:
56    # this only happens when the threadlocal extension is used
57    objectstore = sqlalchemy.objectstore
58except AttributeError:
59    objectstore = Objectstore(SessionContext(sqlalchemy.orm.create_session))
60
61metadatas = set()
62
63
64def create_all(engine=None):
65    'Create all necessary tables for all declared entities'
66    for md in metadatas:
67        md.create_all(bind=engine)
68
69
70def drop_all(engine=None):
71    'Drop all tables for all declared entities'
72    for md in metadatas:
73        md.drop_all(bind=engine)
74
75_delayed_entities = list()
76
77def setup_entities(entities):
78    '''Setup all entities passed in entities'''
79
80    for entity in entities:
81        desc = entity._descriptor
82        entity._ready = False
83        del sqlalchemy.orm.mapper_registry[entity._class_key]
84
85        md = desc.metadata
86
87        # the fake table could have already been removed (namely in a
88        # single table inheritance scenario)
89        md.tables.pop(entity._table_key, None)
90
91        # restore original table iterator if not done already
92        if hasattr(md.table_iterator, '_non_elixir_patched_iterator'):
93            md.table_iterator = \
94                md.table_iterator._non_elixir_patched_iterator
95
96    for method_name in (
97            'setup_autoload_table', 'create_pk_cols', 'setup_relkeys',
98            'before_table', 'setup_table', 'setup_reltables', 'after_table',
99            'setup_events',
100            'before_mapper', 'setup_mapper', 'after_mapper',
101            'setup_properties',
102            'finalize'):
103        for entity in entities:
104            method = getattr(entity._descriptor, method_name)
105            method()
106
107def setup_all(create_tables=False):
108    '''Setup the table and mapper for all entities which have been delayed.
109
110    This is called automatically when any of your entities is first accessed,
111    instanciated (called) or the create_all method of a metadata containing
112    entities is called.
113    '''
114    if not _delayed_entities:
115        return
116
117    try:
118        setup_entities(_delayed_entities)
119    finally:
120        # make sure that even if we fail to initialize, we don't leave junk for
121        # others
122        del _delayed_entities[:]
123
124    # issue the "CREATE" SQL statements
125    if create_tables:
126        create_all()
127
128
129def cleanup_all(drop_tables=False):
130    '''Clear all mappers, clear the session, and clear all metadatas.
131    Optionally drops the tables.
132    '''
133    if drop_tables:
134        drop_all()
135       
136    for md in metadatas:
137        md.clear()
138    metadatas.clear()
139
140    objectstore.clear()
141    sqlalchemy.orm.clear_mappers()
Note: See TracBrowser for help on using the browser.