summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/hybrid.py
blob: b499490718859425ca07bc7cbc0466046a90d208 (plain)
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
"""Define attributes on ORM-mapped classes that have 'hybrid' behavior.

'hybrid' means the attribute has distinct behaviors defined at the
class level and at the instance level.

Consider a table `interval` as below::

    from sqlalchemy import MetaData, Table, Column, Integer
    from sqlalchemy.orm import mapper, create_session
    
    engine = create_engine('sqlite://')
    metadata = MetaData()

    interval_table = Table('interval', metadata,
        Column('id', Integer, primary_key=True),
        Column('start', Integer, nullable=False),
        Column('end', Integer, nullable=False))
    metadata.create_all(engine)
    
We can define higher level functions on mapped classes that produce SQL
expressions at the class level, and Python expression evaluation at the
instance level.  Below, each function decorated with :func:`hybrid.method`
or :func:`hybrid.property` may receive ``self`` as an instance of the class,
or as the class itself::
    
    # A base class for intervals

    from sqlalchemy.orm import hybrid
    
    class Interval(object):
        def __init__(self, start, end):
            self.start = start
            self.end = end
        
        @hybrid.property
        def length(self):
            return self.end - self.start

        @hybrid.method
        def contains(self,point):
            return (self.start <= point) & (point < self.end)
    
        @hybrid.method
        def intersects(self, other):
            return self.contains(other.start) | self.contains(other.end)


        
"""
from sqlalchemy import util
from sqlalchemy.orm import attributes, interfaces

class method(object):
    def __init__(self, func, expr=None):
        self.func = func
        self.expr = expr or func
        
    def __get__(self, instance, owner):
        if instance is None:
            return new.instancemethod(self.expr, owner, owner.__class__)
        else:
            return new.instancemethod(self.func, instance, owner)

    def expression(self, expr):
        self.expr = expr
        return self

class property_(object):
    def __init__(self, fget, fset=None, fdel=None, expr=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.expr = expr or fget
        util.update_wrapper(self, fget)

    def __get__(self, instance, owner):
        if instance is None:
            return self.expr(owner)
        else:
            return self.fget(instance)
            
    def __set__(self, instance, value):
        self.fset(instance, value)
        
    def __delete__(self, instance):
        self.fdel(instance)
    
    def setter(self, fset):
        self.fset = fset
        return self

    def deleter(self, fdel):
        self.fdel = fdel
        return self
    
    def expression(self, expr):
        self.expr = expr
        return self
    
    def comparator(self, comparator):
        proxy_attr = attributes.\
                        create_proxied_attribute(self)
        def expr(owner):
            return proxy_attr(self.__name__, self, comparator(owner))
        self.expr = expr
        return self


class Comparator(interfaces.PropComparator):
    def __init__(self, expression):
        self.expression = expression
      
    def __clause_element__(self):
        expr = self.expression
        while hasattr(expr, '__clause_element__'):
            expr = expr.__clause_element__()
        return expr
            
    def adapted(self, adapter):
        # interesting....
        return self