~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/orm/descriptor_props.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# orm/descriptor_props.py
2
 
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
 
2
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
3
3
#
4
4
# This module is part of SQLAlchemy and is released under
5
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
10
10
 
11
11
"""
12
12
 
13
 
from sqlalchemy.orm.interfaces import \
14
 
    MapperProperty, PropComparator, StrategizedProperty
15
 
from sqlalchemy.orm.mapper import _none_set
16
 
from sqlalchemy.orm import attributes, strategies
17
 
from sqlalchemy import util, sql, exc as sa_exc, event, schema
18
 
from sqlalchemy.sql import expression
 
13
from .interfaces import MapperProperty, PropComparator
 
14
from .util import _none_set
 
15
from . import attributes, strategies
 
16
from .. import util, sql, exc as sa_exc, event, schema
 
17
from ..sql import expression
19
18
properties = util.importlater('sqlalchemy.orm', 'properties')
20
19
 
 
20
 
21
21
class DescriptorProperty(MapperProperty):
22
22
    """:class:`.MapperProperty` which proxies access to a
23
23
        user-defined descriptor."""
30
30
        class _ProxyImpl(object):
31
31
            accepts_scalar_loader = False
32
32
            expire_missing = True
 
33
            collection = False
33
34
 
34
35
            def __init__(self, key):
35
36
                self.key = key
47
48
        if self.descriptor is None:
48
49
            def fset(obj, value):
49
50
                setattr(obj, self.name, value)
 
51
 
50
52
            def fdel(obj):
51
53
                delattr(obj, self.name)
 
54
 
52
55
            def fget(obj):
53
56
                return getattr(obj, self.name)
54
57
 
65
68
                        self.key,
66
69
                        self.descriptor,
67
70
                        lambda: self._comparator_factory(mapper),
68
 
                        doc=self.doc
 
71
                        doc=self.doc,
 
72
                        original_property=self
69
73
                    )
70
74
        proxy_attr.impl = _ProxyImpl(self.key)
71
75
        mapper.class_manager.instrument_attribute(self.key, proxy_attr)
72
76
 
73
77
 
74
78
class CompositeProperty(DescriptorProperty):
75
 
 
 
79
    """Defines a "composite" mapped attribute, representing a collection
 
80
    of columns as one attribute.
 
81
 
 
82
    :class:`.CompositeProperty` is constructed using the :func:`.composite`
 
83
    function.
 
84
 
 
85
    See also:
 
86
 
 
87
    :ref:`mapper_composite`
 
88
 
 
89
    """
76
90
    def __init__(self, class_, *attrs, **kwargs):
77
91
        self.attrs = attrs
78
92
        self.composite_class = class_
81
95
        self.group = kwargs.get('group', None)
82
96
        self.comparator_factory = kwargs.pop('comparator_factory',
83
97
                                            self.__class__.Comparator)
 
98
        if 'info' in kwargs:
 
99
            self.info = kwargs.pop('info')
 
100
 
84
101
        util.set_creation_order(self)
85
102
        self._create_descriptor()
86
103
 
110
127
                # key not present.  Iterate through related
111
128
                # attributes, retrieve their values.  This
112
129
                # ensures they all load.
113
 
                values = [getattr(instance, key) for key in self._attribute_keys]
 
130
                values = [
 
131
                    getattr(instance, key)
 
132
                    for key in self._attribute_keys
 
133
                ]
114
134
 
115
135
                # current expected behavior here is that the composite is
116
136
                # created on access if the object is persistent or if
225
245
            state.dict.pop(self.key, None)
226
246
 
227
247
        event.listen(self.parent, 'after_insert',
228
 
                                    insert_update_handler, raw=True)
 
248
            insert_update_handler, raw=True)
229
249
        event.listen(self.parent, 'after_update',
230
 
                                    insert_update_handler, raw=True)
231
 
        event.listen(self.parent, 'load', load_handler, raw=True, propagate=True)
232
 
        event.listen(self.parent, 'refresh', load_handler, raw=True, propagate=True)
233
 
        event.listen(self.parent, "expire", expire_handler, raw=True, propagate=True)
 
250
            insert_update_handler, raw=True)
 
251
        event.listen(self.parent, 'load',
 
252
            load_handler, raw=True, propagate=True)
 
253
        event.listen(self.parent, 'refresh',
 
254
            load_handler, raw=True, propagate=True)
 
255
        event.listen(self.parent, 'expire',
 
256
            expire_handler, raw=True, propagate=True)
234
257
 
235
258
        # TODO: need a deserialize hook here
236
259
 
271
294
            )
272
295
        else:
273
296
            return attributes.History(
274
 
                (),[self.composite_class(*added)], ()
 
297
                (), [self.composite_class(*added)], ()
275
298
            )
276
299
 
277
300
    def _comparator_factory(self, mapper):
278
 
        return self.comparator_factory(self)
 
301
        return self.comparator_factory(self, mapper)
279
302
 
280
303
    class Comparator(PropComparator):
281
 
        def __init__(self, prop, adapter=None):
282
 
            self.prop = self.property = prop
283
 
            self.adapter = adapter
 
304
        """Produce boolean, comparison, and other operators for
 
305
        :class:`.CompositeProperty` attributes.
 
306
 
 
307
        See the example in :ref:`composite_operations` for an overview
 
308
        of usage , as well as the documentation for :class:`.PropComparator`.
 
309
 
 
310
        See also:
 
311
 
 
312
        :class:`.PropComparator`
 
313
 
 
314
        :class:`.ColumnOperators`
 
315
 
 
316
        :ref:`types_operators`
 
317
 
 
318
        :attr:`.TypeEngine.comparator_factory`
 
319
 
 
320
        """
284
321
 
285
322
        def __clause_element__(self):
286
 
            if self.adapter:
287
 
                # TODO: test coverage for adapted composite comparison
288
 
                return expression.ClauseList(
289
 
                            *[self.adapter(x) for x in self.prop._comparable_elements])
290
 
            else:
291
 
                return expression.ClauseList(*self.prop._comparable_elements)
 
323
            return expression.ClauseList(group=False, *self._comparable_elements)
292
324
 
293
325
        __hash__ = None
294
326
 
 
327
        @util.memoized_property
 
328
        def _comparable_elements(self):
 
329
            if self.adapter:
 
330
                # we need to do a little fudging here because
 
331
                # the adapter function we're given only accepts
 
332
                # ColumnElements, but our prop._comparable_elements is returning
 
333
                # InstrumentedAttribute, because we support the use case
 
334
                # of composites that refer to relationships.  The better
 
335
                # solution here is to open up how AliasedClass interacts
 
336
                # with PropComparators so more context is available.
 
337
                return [self.adapter(x.__clause_element__())
 
338
                            for x in self.prop._comparable_elements]
 
339
            else:
 
340
                return self.prop._comparable_elements
 
341
 
295
342
        def __eq__(self, other):
296
343
            if other is None:
297
344
                values = [None] * len(self.prop._comparable_elements)
298
345
            else:
299
346
                values = other.__composite_values__()
300
 
            return sql.and_(
301
 
                    *[a==b for a, b in zip(self.prop._comparable_elements, values)])
 
347
            comparisons = [
 
348
                a == b
 
349
                for a, b in zip(self.prop._comparable_elements, values)
 
350
            ]
 
351
            if self.adapter:
 
352
                comparisons = [self.adapter(x) for x in comparisons]
 
353
            return sql.and_(*comparisons)
302
354
 
303
355
        def __ne__(self, other):
304
356
            return sql.not_(self.__eq__(other))
306
358
    def __str__(self):
307
359
        return str(self.parent.class_.__name__) + "." + self.key
308
360
 
 
361
 
309
362
class ConcreteInheritedProperty(DescriptorProperty):
310
363
    """A 'do nothing' :class:`.MapperProperty` that disables
311
364
    an attribute on a concrete subclass that is only present
343
396
        class NoninheritedConcreteProp(object):
344
397
            def __set__(s, obj, value):
345
398
                warn()
 
399
 
346
400
            def __delete__(s, obj):
347
401
                warn()
 
402
 
348
403
            def __get__(s, obj, owner):
349
404
                if obj is None:
350
405
                    return self.descriptor
409
464
 
410
465
        self.parent = parent
411
466
 
 
467
 
412
468
class ComparableProperty(DescriptorProperty):
413
469
    """Instruments a Python property for use in query expressions."""
414
470