summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/hybrid.py
blob: e0b3dab4833fa240c1b10c014e891f2e3b42789e (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""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.start < other.end) & (self.end > other.start)

    mapper(Interval1, interval_table1)

    session = sessionmaker(engine)()

    session.add_all(
        [Interval1(1,4), Interval1(3,15), Interval1(11,16)]
    )
    intervals = 

    for interval in intervals:
        session.add(interval)
        session.add(Interval2(interval.start, interval.length))

    session.commit()

    ### TODO ADD EXAMPLES HERE AND STUFF THIS ISN'T FINISHED ###
    
"""
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
        

def hybrid_and(self):
    if isinstance(self, type):
        return expression.and_
    else:
        return operator.and_