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

Revision 347, 6.7 kB (checked in by ged, 6 years ago)

removed trailing spaces

Line 
1'''
2Elixir package
3
4A declarative layer on top of the `SQLAlchemy library
5<http://www.sqlalchemy.org/>`_. It is a fairly thin wrapper, which provides
6the ability to create simple Python classes that map directly to relational
7database tables (this pattern is often referred to as the Active Record design
8pattern), providing many of the benefits of traditional databases
9without losing the convenience of Python objects.
10
11Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the
12TurboEntity project but does not intend to replace SQLAlchemy's core features,
13and instead focuses on providing a simpler syntax for defining model objects
14when you do not need the full expressiveness of SQLAlchemy's manual mapper
15definitions.
16'''
17
18try:
19    set
20except NameError:
21    from sets import Set as set
22
23import sys
24import warnings
25
26from py23compat import rsplit
27
28import sqlalchemy
29from sqlalchemy.types import *
30
31from elixir.options import using_options, using_table_options, \
32                           using_mapper_options, options_defaults
33from elixir.entity import Entity, EntityMeta, EntityDescriptor, \
34                          setup_entities, cleanup_entities
35from elixir.fields import has_field, with_fields, Field
36from elixir.relationships import belongs_to, has_one, has_many, \
37                                 has_and_belongs_to_many, \
38                                 ManyToOne, OneToOne, OneToMany, ManyToMany
39from elixir.properties import has_property, GenericProperty, ColumnProperty, \
40                              Synonym
41from elixir.statements import Statement
42
43
44__version__ = '0.6.0'
45
46__all__ = ['Entity', 'EntityMeta', 'EntityCollection', 'entities',
47           'Field', 'has_field', 'with_fields',
48           'has_property', 'GenericProperty', 'ColumnProperty', 'Synonym',
49           'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many',
50           'ManyToOne', 'OneToOne', 'OneToMany', 'ManyToMany',
51           'using_options', 'using_table_options', 'using_mapper_options',
52           'options_defaults', 'metadata', 'objectstore', 'session',
53           'create_all', 'drop_all',
54           'setup_all', 'cleanup_all',
55           'setup_entities', 'cleanup_entities'] + \
56           sqlalchemy.types.__all__
57
58__doc_all__ = ['create_all', 'drop_all',
59               'setup_all', 'cleanup_all',
60               'metadata', 'session']
61
62
63class Objectstore(object):
64    """a wrapper for a SQLAlchemy session-making object, such as
65    SessionContext or ScopedSession.
66
67    Uses the ``registry`` attribute present on both objects
68    (versions 0.3 and 0.4) in order to return the current
69    contextual session.
70    """
71
72    def __init__(self, ctx):
73        self.context = ctx
74
75    def __getattr__(self, name):
76        return getattr(self.context.registry(), name)
77
78    session = property(lambda s:s.context.registry())
79
80# default session
81try:
82    from sqlalchemy.orm import scoped_session
83    session = scoped_session(sqlalchemy.orm.create_session)
84except ImportError:
85    # Not on version 0.4 of sqlalchemy
86    from sqlalchemy.ext.sessioncontext import SessionContext
87    session = Objectstore(SessionContext(sqlalchemy.orm.create_session))
88
89# backward-compatible name
90objectstore = session
91
92# default metadata
93metadata = sqlalchemy.MetaData()
94
95metadatas = set()
96
97# default entity collection
98class EntityCollection(list):
99    def __init__(self):
100        # _entities is a dict of entities for each frame where there are
101        # entities defined.
102        self._entities = {}
103#        self._entity_map = {}
104        list.__init__(self)
105
106    def map_entity(self, entity):
107        self.append(entity)
108
109        key = entity.__name__
110
111#        if key in self._entity_map:
112#            warnings.warn('An entity named `%s` is already registered!' % key)
113
114        # 3 is because map_entity is called by:
115        # EntityDescriptor::setup_options (which is called by)
116        # EntityMeta::__init__
117        # which is called when the entity is defined
118        caller_frame = sys._getframe(3)
119        cid = entity._caller = id(caller_frame)
120        caller_entities = self._entities.setdefault(cid, {})
121        caller_entities[key] = entity
122
123        # Append all entities which are currently visible by the entity. This
124        # will find more entities only if some of them where imported from
125        # another module.
126        for ent in [e for e in caller_frame.f_locals.values()
127                         if isinstance(e, EntityMeta)]:
128            caller_entities[ent.__name__] = ent
129
130#        self._entity_map[key] = entity
131
132    def resolve(self, key, entity=None):
133        '''
134        Resolve a key to an Entity. The optional `entity` argument is the
135        "source" entity when resolving relationship targets.
136        '''
137        path = rsplit(key, '.', 1)
138        classname = path.pop()
139        #XXX: use eval()?
140
141        if path:
142            # Do we have a fully qualified entity name?
143            module = sys.modules[path.pop()]
144            return getattr(module, classname, None)
145        else:
146            # If not, try the list of entities of the "caller" of the
147            # source class. Most of the time, this will be the module
148            # the class is defined in. But it could also be a method
149            # (inner classes).
150            caller_entities = self._entities[entity._caller]
151            return caller_entities[classname]
152
153    def __getattr__(self, key):
154        print "GLOBALS", globals().keys()
155        print "LOCALS", locals().keys()
156        return self.resolve(key)
157#        return self._entity_map.get(key)
158
159entities = EntityCollection()
160
161
162def create_all(*args, **kwargs):
163    '''Create the necessary tables for all declared entities'''
164    for md in metadatas:
165        md.create_all(*args, **kwargs)
166
167
168def drop_all(*args, **kwargs):
169    '''Drop tables for all declared entities'''
170    for md in metadatas:
171        md.drop_all(*args, **kwargs)
172
173
174def setup_all(create_tables=False, *args, **kwargs):
175    '''Setup the table and mapper of all entities in the default entity
176    collection.
177
178    This is called automatically if any entity of the collection is configured
179    with the `autosetup` option and it is first accessed,
180    instanciated (called) or the create_all method of a metadata containing
181    tables from any of those entities is called.
182    '''
183    setup_entities(entities)
184
185    # issue the "CREATE" SQL statements
186    if create_tables:
187        create_all(*args, **kwargs)
188
189
190def cleanup_all(drop_tables=False, *args, **kwargs):
191    '''Clear all mappers, clear the session, and clear all metadatas.
192    Optionally drops the tables.
193    '''
194    if drop_tables:
195        drop_all(*args, **kwargs)
196
197    cleanup_entities(entities)
198
199    for md in metadatas:
200        md.clear()
201    metadatas.clear()
202
203    session.clear()
204
205    sqlalchemy.orm.clear_mappers()
206    del entities[:]
Note: See TracBrowser for help on using the browser.