forked from CitoyensCapteurs/CitizenWatt-Base
-
Notifications
You must be signed in to change notification settings - Fork 2
/
bottle_sqlalchemy.py
163 lines (133 loc) · 5.58 KB
/
bottle_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
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
'''
This bottle-sqlalchemy plugin integrates SQLAlchemy with your Bottle
application. It connects to a database at the beginning of a request,
passes the database handle to the route callback and closes the connection
afterwards.
The plugin inject an argument to all route callbacks that require a `db`
keyword.
Usage Example::
import bottle
from bottle import HTTPError
from bottle.ext import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, Sequence, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
app = bottle.Bottle()
plugin = sqlalchemy.Plugin(engine, Base.metadata, create=True)
app.install(plugin)
class Entity(Base):
__tablename__ = 'entity'
id = Column(Integer, Sequence('id_seq'), primary_key=True)
name = Column(String(50))
def __init__(self, name):
self.name = name
def __repr__(self):
return "<Entity('%d', '%s')>" % (self.id, self.name)
@app.get('/:name')
def show(name, db):
entity = db.query(Entity).filter_by(name=name).first()
if entity:
return {'id': entity.id, 'name': entity.name}
return HTTPError(404, 'Entity not found.')
@app.put('/:name')
def put_name(name, db):
entity = Entity(name)
db.add(entity)
It is up to you create engine and metadata, because SQLAlchemy has
a lot of options to do it. The plugin just handles the SQLAlchemy
session.
Copyright (c) 2011-2012, Iuri de Silvio
License: MIT (see LICENSE for details)
'''
import inspect
import bottle
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import ScopedSession
# PluginError is defined to bottle >= 0.10
if not hasattr(bottle, 'PluginError'):
class PluginError(bottle.BottleException):
pass
bottle.PluginError = PluginError
class SQLAlchemyPlugin(object):
name = 'sqlalchemy'
api = 2
def __init__(self, engine, metadata=None,
keyword='db', commit=True, create=False, use_kwargs=False, create_session=None):
'''
:param engine: SQLAlchemy engine created with `create_engine` function
:param metadata: SQLAlchemy metadata. It is required only if `create=True`
:param keyword: Keyword used to inject session database in a route
:param create: If it is true, execute `metadata.create_all(engine)`
when plugin is applied
:param commit: If it is true, commit changes after route is executed.
:param use_kwargs: plugin inject session database even if it is not
explicitly defined, using **kwargs argument if defined.
:param create_session: SQLAlchemy session maker created with the
'sessionmaker' function. Will create its own if undefined.
'''
self.engine = engine
if create_session is None:
create_session = sessionmaker()
self.create_session = create_session
self.metadata = metadata
self.keyword = keyword
self.create = create
self.commit = commit
self.use_kwargs = use_kwargs
def setup(self, app):
''' Make sure that other installed plugins don't affect the same
keyword argument and check if metadata is available.'''
for other in app.plugins:
if not isinstance(other, SQLAlchemyPlugin):
continue
if other.keyword == self.keyword:
raise bottle.PluginError("Found another SQLAlchemy plugin with "\
"conflicting settings (non-unique keyword).")
elif other.name == self.name:
self.name += '_%s' % self.keyword
if self.create and not self.metadata:
raise bottle.PluginError('Define metadata value to create database.')
def apply(self, callback, route):
# hack to support bottle v0.9.x
if bottle.__version__.startswith('0.9'):
config = route['config']
_callback = route['callback']
else:
config = route.config
_callback = route.callback
if "sqlalchemy" in config: # support for configuration before `ConfigDict` namespaces
g = lambda key, default: config.get('sqlalchemy', {}).get(key, default)
else:
g = lambda key, default: config.get('sqlalchemy.' + key, default)
keyword = g('keyword', self.keyword)
create = g('create', self.create)
commit = g('commit', self.commit)
use_kwargs = g('use_kwargs', self.use_kwargs)
argspec = inspect.getargspec(_callback)
if not ((use_kwargs and argspec.keywords) or keyword in argspec.args):
return callback
if create:
self.metadata.create_all(self.engine)
def wrapper(*args, **kwargs):
kwargs[keyword] = session = self.create_session(bind=self.engine)
try:
rv = callback(*args, **kwargs)
if commit:
session.commit()
except (SQLAlchemyError, bottle.HTTPError):
session.rollback()
raise
except bottle.HTTPResponse:
if commit:
session.commit()
raise
finally:
if isinstance(self.create_session, ScopedSession):
self.create_session.remove()
else:
session.close()
return rv
return wrapper
Plugin = SQLAlchemyPlugin