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

Revision 199, 4.7 kB (checked in by ged, 6 years ago)
  • Added test for the case when you refer to a remotely-defined class by its
    named after importing it into the local namespace.
  • Implemented a new syntax to declare fields and relationships, much closer to
    what is found in other Python ORM's. The with_fields syntax is now
    deprecated in favor of a that new syntax. The old statement-based (has_field et
    al.) syntax stays the default for now. This was done with help from a patch
    by Adam Gomaa.
  • Relationships to other classes can now also be defined using the classes
    themselves in addition to the class names. Obviously, this doesn't work for
    forward references.
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.relationships import ManyToOne, OneToOne, OneToMany, ManyToMany
31from elixir.properties import has_property
32from elixir.statements import Statement
33
34try:
35    set
36except NameError:
37    from sets import Set as set
38
39__version__ = '0.4.0'
40
41__all__ = ['Entity', 'EntityMeta', 'Field', 'has_field', 'with_fields',
42           'has_property', 
43           'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many',
44           'ManyToOne', 'OneToOne', 'OneToMany', 'ManyToMany',
45           'using_options', 'using_table_options', 'using_mapper_options',
46           'options_defaults', 'metadata', 'objectstore',
47           'create_all', 'drop_all',
48           'setup_all', 'cleanup_all'] + sqlalchemy.types.__all__
49
50__pudge_all__ = ['create_all', 'drop_all',
51                 'setup_all', 'cleanup_all',
52                 'metadata', 'objectstore']
53
54# connect
55metadata = sqlalchemy.MetaData()
56
57try:
58    # this only happens when the threadlocal extension is used
59    objectstore = sqlalchemy.objectstore
60except AttributeError:
61    objectstore = Objectstore(SessionContext(sqlalchemy.orm.create_session))
62
63metadatas = set()
64
65
66def create_all(engine=None):
67    'Create all necessary tables for all declared entities'
68    for md in metadatas:
69        md.create_all(bind=engine)
70
71
72def drop_all(engine=None):
73    'Drop all tables for all declared entities'
74    for md in metadatas:
75        md.drop_all(bind=engine)
76
77_delayed_entities = list()
78
79def setup_entities(entities):
80    '''Setup all entities passed in entities'''
81
82    for entity in entities:
83        desc = entity._descriptor
84        entity._ready = False
85        del sqlalchemy.orm.mapper_registry[entity._class_key]
86
87        md = desc.metadata
88
89        # the fake table could have already been removed (namely in a
90        # single table inheritance scenario)
91        md.tables.pop(entity._table_key, None)
92
93        # restore original table iterator if not done already
94        if hasattr(md.table_iterator, '_non_elixir_patched_iterator'):
95            md.table_iterator = \
96                md.table_iterator._non_elixir_patched_iterator
97
98    for method_name in (
99            'setup_autoload_table', 'create_pk_cols', 'setup_relkeys',
100            'before_table', 'setup_table', 'setup_reltables', 'after_table',
101            'setup_events',
102            'before_mapper', 'setup_mapper', 'after_mapper',
103            'setup_properties',
104            'finalize'):
105        for entity in entities:
106            method = getattr(entity._descriptor, method_name)
107            method()
108
109def setup_all(create_tables=False):
110    '''Setup the table and mapper for all entities which have been delayed.
111
112    This is called automatically when any of your entities is first accessed,
113    instanciated (called) or the create_all method of a metadata containing
114    entities is called.
115    '''
116    if not _delayed_entities:
117        return
118
119    try:
120        setup_entities(_delayed_entities)
121    finally:
122        # make sure that even if we fail to initialize, we don't leave junk for
123        # others
124        del _delayed_entities[:]
125
126    # issue the "CREATE" SQL statements
127    if create_tables:
128        create_all()
129
130
131def cleanup_all(drop_tables=False):
132    '''Clear all mappers, clear the session, and clear all metadatas.
133    Optionally drops the tables.
134    '''
135    if drop_tables:
136        drop_all()
137       
138    for md in metadatas:
139        md.clear()
140    metadatas.clear()
141
142    objectstore.clear()
143    sqlalchemy.orm.clear_mappers()
Note: See TracBrowser for help on using the browser.