1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
# mapper/__init__.py
# Copyright (C) 2005, 2006, 2007 Michael Bayer mike_mp@zzzcomputing.com
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
"""
The mapper package provides object-relational functionality, building upon the schema and sql
packages and tying operations to class properties and constructors.
"""
from sqlalchemy import exceptions
from sqlalchemy import util as sautil
from sqlalchemy.orm.mapper import *
from sqlalchemy.orm import mapper as mapperlib
from sqlalchemy.orm.query import Query
from sqlalchemy.orm.util import polymorphic_union
from sqlalchemy.orm import properties, strategies, interfaces
from sqlalchemy.orm.session import Session as create_session
from sqlalchemy.orm.session import object_session, attribute_manager
__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension',
'mapper', 'clear_mappers', 'compile_mappers', 'clear_mapper', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query',
'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_alias', 'contains_eager', 'EXT_PASS', 'object_session'
]
def relation(*args, **kwargs):
"""Provide a relationship of a primary Mapper to a secondary Mapper.
This corresponds to a parent-child or associative table relationship.
"""
if len(args) > 1 and isinstance(args[0], type):
raise exceptions.ArgumentError("relation(class, table, **kwargs) is deprecated. Please use relation(class, **kwargs) or relation(mapper, **kwargs).")
return _relation_loader(*args, **kwargs)
def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs):
return properties.PropertyLoader(mapper, secondary, primaryjoin, secondaryjoin, lazy=lazy, **kwargs)
def backref(name, **kwargs):
"""Create a BackRef object with explicit arguments, which are the same arguments one
can send to ``relation()``.
Used with the `backref` keyword argument to ``relation()`` in
place of a string argument.
"""
return properties.BackRef(name, **kwargs)
def deferred(*columns, **kwargs):
"""Return a ``DeferredColumnProperty``, which indicates this
object attributes should only be loaded from its corresponding
table column when first accessed.
Used with the `properties` dictionary sent to ``mapper()``.
"""
return properties.ColumnProperty(deferred=True, *columns, **kwargs)
def mapper(class_, table=None, *args, **params):
"""Return a new ``Mapper`` object.
See the ``Mapper`` class for a description of arguments.
"""
return Mapper(class_, table, *args, **params)
def synonym(name, proxy=False):
"""Set up `name` as a synonym to another ``MapperProperty``.
Used with the `properties` dictionary sent to ``mapper()``.
"""
return interfaces.SynonymProperty(name, proxy=proxy)
def compile_mappers():
"""Compile all mappers that have been defined.
This is equivalent to calling ``compile()` on any individual mapper.
"""
if not len(mapper_registry):
return
mapper_registry.values()[0].compile()
def clear_mappers():
"""Remove all mappers that have been created thus far.
When new mappers are created, they will be assigned to their
classes as their primary mapper.
"""
for mapper in mapper_registry.values():
attribute_manager.reset_class_managed(mapper.class_)
if hasattr(mapper.class_, 'c'):
del mapper.class_.c
mapper_registry.clear()
sautil.ArgSingleton.instances.clear()
def clear_mapper(m):
"""Remove the given mapper from the storage of mappers.
When a new mapper is created for the previous mapper's class, it
will be used as that classes' new primary mapper.
"""
del mapper_registry[m.class_key]
attribute_manager.reset_class_managed(m.class_)
if hasattr(m.class_, 'c'):
del m.class_.c
m.class_key.dispose()
def extension(ext):
"""Return a ``MapperOption`` that will insert the given
``MapperExtension`` to the beginning of the list of extensions
that will be called in the context of the ``Query``.
Used with ``query.options()``.
"""
return ExtensionOption(ext)
def eagerload(name):
"""Return a ``MapperOption`` that will convert the property of the
given name into an eager load.
Used with ``query.options()``.
"""
return strategies.EagerLazyOption(name, lazy=False)
def lazyload(name):
"""Return a ``MapperOption`` that will convert the property of the
given name into a lazy load.
Used with ``query.options()``.
"""
return strategies.EagerLazyOption(name, lazy=True)
def noload(name):
"""Return a ``MapperOption`` that will convert the property of the
given name into a non-load.
Used with ``query.options()``.
"""
return strategies.EagerLazyOption(name, lazy=None)
def contains_alias(alias):
"""Return a ``MapperOption`` that will indicate to the query that
the main table has been aliased.
`alias` is the string name or ``Alias`` object representing the
alias.
"""
class AliasedRow(MapperExtension):
def __init__(self, alias):
self.alias = alias
if isinstance(self.alias, basestring):
self.selectable = None
else:
self.selectable = alias
def get_selectable(self, mapper):
if self.selectable is None:
self.selectable = mapper.mapped_table.alias(self.alias)
return self.selectable
def translate_row(self, mapper, context, row):
newrow = sautil.DictDecorator(row)
selectable = self.get_selectable(mapper)
for c in mapper.mapped_table.c:
c2 = selectable.corresponding_column(c, keys_ok=True, raiseerr=False)
if c2 and row.has_key(c2):
newrow[c] = row[c2]
return newrow
return ExtensionOption(AliasedRow(alias))
def contains_eager(key, alias=None, decorator=None):
"""Return a ``MapperOption`` that will indicate to the query that
the given attribute will be eagerly loaded.
Used when feeding SQL result sets directly into
``query.instances()``. Also bundles an ``EagerLazyOption`` to
turn on eager loading in case it isnt already.
`alias` is the string name of an alias, **or** an ``sql.Alias``
object, which represents the aliased columns in the query. This
argument is optional.
`decorator` is mutually exclusive of `alias` and is a
row-processing function which will be applied to the incoming row
before sending to the eager load handler. use this for more
sophisticated row adjustments beyond a straight alias.
"""
return (strategies.EagerLazyOption(key, lazy=False), strategies.RowDecorateOption(key, alias=alias, decorator=decorator))
def defer(name):
"""Return a ``MapperOption`` that will convert the column property
of the given name into a deferred load.
Used with ``query.options()``"""
return strategies.DeferredOption(name, defer=True)
def undefer(name):
"""Return a ``MapperOption`` that will convert the column property
of the given name into a non-deferred (regular column) load.
Used with ``query.options()``.
"""
return strategies.DeferredOption(name, defer=False)
def cascade_mappers(*classes_or_mappers):
"""Attempt to create a series of ``relations()`` between mappers
automatically, via introspecting the foreign key relationships of
the underlying tables.
Given a list of classes and/or mappers, identify the foreign key
relationships between the given mappers or corresponding class
mappers, and create ``relation()`` objects representing those
relationships, including a backreference. Attempt to find the
*secondary* table in a many-to-many relationship as well.
The names of the relations will be a lowercase version of the
related class. In the case of one-to-many or many-to-many, the
name will be *pluralized*, which currently is based on the English
language (i.e. an 's' or 'es' added to it).
NOTE: this method usually works poorly, and its usage is generally
not advised.
"""
table_to_mapper = {}
for item in classes_or_mappers:
if isinstance(item, Mapper):
m = item
else:
klass = item
m = class_mapper(klass)
table_to_mapper[m.mapped_table] = m
def pluralize(name):
# oh crap, do we need locale stuff now
if name[-1] == 's':
return name + "es"
else:
return name + "s"
for table,mapper in table_to_mapper.iteritems():
for fk in table.foreign_keys:
if fk.column.table is table:
continue
secondary = None
try:
m2 = table_to_mapper[fk.column.table]
except KeyError:
if len(fk.column.table.primary_key):
continue
for sfk in fk.column.table.foreign_keys:
if sfk.column.table is table:
continue
m2 = table_to_mapper.get(sfk.column.table)
secondary = fk.column.table
if m2 is None:
continue
if secondary:
propname = pluralize(m2.class_.__name__.lower())
propname2 = pluralize(mapper.class_.__name__.lower())
else:
propname = m2.class_.__name__.lower()
propname2 = pluralize(mapper.class_.__name__.lower())
mapper.add_property(propname, relation(m2, secondary=secondary, backref=propname2))
|