Bottle framework and OOP, using method instead of function
Question:
I’ve done some coding with Bottle. It’s really simple and fits my needs. However, I got stick when I tried to wrap the application into a class :
import bottle
app = bottle
class App():
def __init__(self,param):
self.param = param
# Doesn't work
@app.route("/1")
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
# Doesn't work
@app.route("/2")
def index2(self):
return("I'm 2")
# Works fine
@app.route("/3")
def index3():
return("I'm 3")
Is it possible to use methods instead of functions in Bottle?
Answers:
Your code does not work because you are trying to route to non-bound methods. Non-bound methods do not have a reference to self
, how could they, if instance of App
has not been created?
If you want to route to class methods, you first have to initialize your class and then bottle.route()
to methods on that object like so:
import bottle
class App(object):
def __init__(self,param):
self.param = param
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
myapp = App(param='some param')
bottle.route("/1")(myapp.index1)
If you want to stick routes definitions near the handlers, you can do something like this:
def routeapp(obj):
for kw in dir(app):
attr = getattr(app, kw)
if hasattr(attr, 'route'):
bottle.route(attr.route)(attr)
class App(object):
def __init__(self, config):
self.config = config
def index(self):
pass
index.route = '/index/'
app = App({'config':1})
routeapp(app)
Don’t do the bottle.route()
part in App.__init__()
, because you won’t be able to create two instances of App
class.
If you like the syntax of decorators more than setting attribute index.route=
, you can write a simple decorator:
def methodroute(route):
def decorator(f):
f.route = route
return f
return decorator
class App(object):
@methodroute('/index/')
def index(self):
pass
Below works nicely for me 🙂
Quite object orientated and easy to follow.
from bottle import Bottle, template
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
self._route()
def _route(self):
self._app.route('/', method="GET", callback=self._index)
self._app.route('/hello/<name>', callback=self._hello)
def start(self):
self._app.run(host=self._host, port=self._port)
def _index(self):
return 'Welcome'
def _hello(self, name="Guest"):
return template('Hello {{name}}, how are you?', name=name)
server = Server(host='localhost', port=8090)
server.start()
I took @Skirmantas answer and modified it a bit to allow for keyword arguments in the decorator, like method, skip, etc:
def routemethod(route, **kwargs):
def decorator(f):
f.route = route
for arg in kwargs:
setattr(f, arg, kwargs[arg])
return f
return decorator
def routeapp(obj):
for kw in dir(obj):
attr = getattr(obj, kw)
if hasattr(attr, "route"):
if hasattr(attr, "method"):
method = getattr(attr, "method")
else:
method = "GET"
if hasattr(attr, "callback"):
callback = getattr(attr, "callback")
else:
callback = None
if hasattr(attr, "name"):
name = getattr(attr, "name")
else:
name = None
if hasattr(attr, "apply"):
aply = getattr(attr, "apply")
else:
aply = None
if hasattr(attr, "skip"):
skip = getattr(attr, "skip")
else:
skip = None
bottle.route(attr.route, method, callback, name, aply, skip)(attr)
try this, worked for me, documentation is also pretty decent to get started with …
https://github.com/techchunks/bottleCBV
You have to extend the Bottle
class. It’s instances are WSGI web applications.
from bottle import Bottle
class MyApp(Bottle):
def __init__(self, name):
super(MyApp, self).__init__()
self.name = name
self.route('/', callback=self.index)
def index(self):
return "Hello, my name is " + self.name
app = MyApp('OOBottle')
app.run(host='localhost', port=8080)
What most examples out there are doing, including the answers previously provided to this question, are all reusing the “default app”, not creating their own, and not using the convenience of object orientation and inheritance.
I know this question is quite old, but it is useful.
I am currently refactoring a Bottle application code into class. I am thinking about using something like:
import bottle
class App:
def __init__(self, port=80):
self.app = bottle.Bottle()
self.port = port
def setup_routes(self):
@self.app.route("/foo")
def foo():
return("foo")
@self.app.route("/bar")
def bar():
return("bar")
def start(self):
self.app.run(port=self.port)
a = App()
a.setup_routes()
a.start()
Does it work for your applications too? Feel free to comment, I am interested about this.
Notes from the creator of Bottle: https://github.com/bottlepy/bottle/issues/1224#issuecomment-619344849
I’ve done some coding with Bottle. It’s really simple and fits my needs. However, I got stick when I tried to wrap the application into a class :
import bottle
app = bottle
class App():
def __init__(self,param):
self.param = param
# Doesn't work
@app.route("/1")
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
# Doesn't work
@app.route("/2")
def index2(self):
return("I'm 2")
# Works fine
@app.route("/3")
def index3():
return("I'm 3")
Is it possible to use methods instead of functions in Bottle?
Your code does not work because you are trying to route to non-bound methods. Non-bound methods do not have a reference to self
, how could they, if instance of App
has not been created?
If you want to route to class methods, you first have to initialize your class and then bottle.route()
to methods on that object like so:
import bottle
class App(object):
def __init__(self,param):
self.param = param
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
myapp = App(param='some param')
bottle.route("/1")(myapp.index1)
If you want to stick routes definitions near the handlers, you can do something like this:
def routeapp(obj):
for kw in dir(app):
attr = getattr(app, kw)
if hasattr(attr, 'route'):
bottle.route(attr.route)(attr)
class App(object):
def __init__(self, config):
self.config = config
def index(self):
pass
index.route = '/index/'
app = App({'config':1})
routeapp(app)
Don’t do the bottle.route()
part in App.__init__()
, because you won’t be able to create two instances of App
class.
If you like the syntax of decorators more than setting attribute index.route=
, you can write a simple decorator:
def methodroute(route):
def decorator(f):
f.route = route
return f
return decorator
class App(object):
@methodroute('/index/')
def index(self):
pass
Below works nicely for me 🙂
Quite object orientated and easy to follow.
from bottle import Bottle, template
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
self._route()
def _route(self):
self._app.route('/', method="GET", callback=self._index)
self._app.route('/hello/<name>', callback=self._hello)
def start(self):
self._app.run(host=self._host, port=self._port)
def _index(self):
return 'Welcome'
def _hello(self, name="Guest"):
return template('Hello {{name}}, how are you?', name=name)
server = Server(host='localhost', port=8090)
server.start()
I took @Skirmantas answer and modified it a bit to allow for keyword arguments in the decorator, like method, skip, etc:
def routemethod(route, **kwargs):
def decorator(f):
f.route = route
for arg in kwargs:
setattr(f, arg, kwargs[arg])
return f
return decorator
def routeapp(obj):
for kw in dir(obj):
attr = getattr(obj, kw)
if hasattr(attr, "route"):
if hasattr(attr, "method"):
method = getattr(attr, "method")
else:
method = "GET"
if hasattr(attr, "callback"):
callback = getattr(attr, "callback")
else:
callback = None
if hasattr(attr, "name"):
name = getattr(attr, "name")
else:
name = None
if hasattr(attr, "apply"):
aply = getattr(attr, "apply")
else:
aply = None
if hasattr(attr, "skip"):
skip = getattr(attr, "skip")
else:
skip = None
bottle.route(attr.route, method, callback, name, aply, skip)(attr)
try this, worked for me, documentation is also pretty decent to get started with …
https://github.com/techchunks/bottleCBV
You have to extend the Bottle
class. It’s instances are WSGI web applications.
from bottle import Bottle
class MyApp(Bottle):
def __init__(self, name):
super(MyApp, self).__init__()
self.name = name
self.route('/', callback=self.index)
def index(self):
return "Hello, my name is " + self.name
app = MyApp('OOBottle')
app.run(host='localhost', port=8080)
What most examples out there are doing, including the answers previously provided to this question, are all reusing the “default app”, not creating their own, and not using the convenience of object orientation and inheritance.
I know this question is quite old, but it is useful.
I am currently refactoring a Bottle application code into class. I am thinking about using something like:
import bottle
class App:
def __init__(self, port=80):
self.app = bottle.Bottle()
self.port = port
def setup_routes(self):
@self.app.route("/foo")
def foo():
return("foo")
@self.app.route("/bar")
def bar():
return("bar")
def start(self):
self.app.run(port=self.port)
a = App()
a.setup_routes()
a.start()
Does it work for your applications too? Feel free to comment, I am interested about this.
Notes from the creator of Bottle: https://github.com/bottlepy/bottle/issues/1224#issuecomment-619344849