-
Notifications
You must be signed in to change notification settings - Fork 0
/
sqlalchemy.py
127 lines (102 loc) · 3.94 KB
/
sqlalchemy.py
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
import functools
from collections import Iterable
from warnings import warn
from sqlalchemy.dialects.postgresql import array
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.orm import Session
from sqlalchemy.sql import FromClause, ClauseElement, bindparam
from sqlalchemy.sql.sqltypes import NULLTYPE
class class_with_session:
def __init__(self, func):
self.exceptions_tuple = (Exception,)
self.on_exception_handler = None
def wrapper(*args, **kw):
session = Session()
if session.transaction is None:
session.begin()
try:
ret = func(db_session=session, *args, **kw)
session.commit()
return ret
except self.exceptions_tuple as e:
session.rollback()
if self.on_exception_handler:
try:
handler_res = self.on_exception_handler(e)
except:
raise
else:
warn('Handler swallowed exception "%s"' % str(e))
return handler_res
else:
raise
self.func = wrapper
def __get__(self, instance, class_):
'''Перехват для методов'''
return functools.partial(self.func, instance)
def __set__(self, instance, value):
'''Сделаем его non-data descriptor'''
pass
def __call__(self, *args, **kwargs):
'''Перехват для функций'''
return self.func(*args, **kwargs)
def on_exception(self, exceptions_tuple=None):
if exceptions_tuple:
exceptions_tuple = tuple(exceptions_tuple) \
if isinstance(exceptions_tuple, Iterable) \
else (exceptions_tuple,)
self.exceptions_tuple = exceptions_tuple
def deco(fn):
self.on_exception_handler = fn
return fn
return deco
def with_session(fn):
'''
Начинает транзакцию, если ещё не начата
Если в функции происходит исключение(по умолчанию - любое), откатывает её
'''
return class_with_session(fn)
class values(FromClause):
named_with_column = True
def __init__(self, columns, *args, **kw):
self._column_args = columns
self.list = args
self.alias_name = self.name = kw.pop('alias_name', None)
def _populate_column_collection(self):
# self._columns.update((col.name, col) for col in self._column_args)
for c in self._column_args:
c._make_proxy(self, c.name)
@compiles(values)
def compile_values(clause, compiler, asfrom=False, **kw):
def decide(value, column):
add_type_hint = False
if isinstance(value, array) and not value.clauses: # for empty array literals
add_type_hint = True
if isinstance(value, ClauseElement):
intermediate = compiler.process(value)
if add_type_hint:
intermediate += '::' + str(column.type)
return intermediate
else:
intermediate = compiler.render_literal_value(
value,
column.type if (value is not None) else NULLTYPE
)
if value is not None:
intermediate = compiler.process(
bindparam(None, value=intermediate)
) + '::' + str(column.type)
return intermediate
columns = clause.columns
v = "VALUES %s" % ", ".join(
"(%s)" % ", ".join(
decide(elem, column)
for elem, column in zip(tup, columns))
for tup in clause.list
)
if asfrom:
if clause.alias_name:
v = "(%s) AS %s (%s)" % (v, clause.alias_name, (", ".join(c.name for c in clause.columns)))
else:
v = "(%s)" % v
return v