sheepdog
sheepdog

Reputation: 625

bottle: use jinja2_view decorator

If I import post, get, and jinja2_view directly from bottle, I'm able to use a jinja2_view as a decorator:

from bottle import get, post, request, run, jinja2_view

@jinja2_view('index.html')
@get('/')
def index():
    return

run(host='localhost', port=8080, debug=True, reloader=True)

But if I import and use the Bottle() constructor, I can no longer use the jinja decorator:

from bottle import Bottle, get, post, request, run, jinja2_view

app = Bottle()

@app.jinja2_view('index.html')
@app.get('/')
def index():
    return

run(app, host='localhost', port=8080, debug=True, reloader=True)

I get:

Traceback (most recent call last):
  File "webserver.py", line 10, in <module>
    @app.jinja2_view('index.html')
AttributeError: 'Bottle' object has no attribute 'jinja2_view'

How can I use jinja2_view with the Bottle() constructor? (the constructor is necessary because I'm using a mysql lib that requires app.install(plugin).

EDIT 1

If I use @app.get() together with @jinja2_view the callback function doesn't have access to the plugin

from bottle import get, post, request, run, jinja2_view, Bottle

import bottle_mysql

app = Bottle()
plugin = bottle_mysql.Plugin(dbuser='bottle',dbpass='password', dbname='mydb')
app.install(plugin)

@app.get('/now')
@jinja2_view('now.html')
def get_now(db):
    db.execute('SELECT now() as now')
    row = db.fetchone()
    now = str(row['now'])
    return { 'now': now }

run(app, host='localhost', port=8080, debug=True, reloader=True)

Exception:

TypeError('get_now() takes exactly 1 argument (0 given)',)

If I comment out @jinja2_view('now.html') the route works and returns the correct json response.

Upvotes: 1

Views: 1270

Answers (2)

user3351605
user3351605

Reputation: 1291

jinja2_view is a function provided by the bottle module, it is not a class method of the Bottle class. Therefore, when you call @app.jinja2_view, python searchs app (which is an instance of bottle.Bottle) for an attribute called jinja2_view, which it obviously fails to find.

So, you have two really easy options to correct this:

  1. You can go back to using @jinja2_view('index.html').
  2. Just import bottle and use fully qualified namespacing for all the bottle methods, e.g. @bottle.jinja2_view('index.html') and app = bottle.Bottle().

I personally have a strong preference for the latter, as it avoids accidental pollution of the global namespace, which can be important as these small projects build around webservers tend to grow and bloat over time. Of course, your mileage may vary.

Edit

I've created an even simpler example based off your original, hopefully this will help determine where the problem lies. Try running this:

from bottle import get, run, jinja2_view, Bottle
import datetime

app = Bottle()
@app.get('/now')
@jinja2_view('now.html')
def get_now():
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return { 'now': now }

run(app, host='localhost', port=8080, debug=True)

Viewing this in the browser renders the datetime string correctly within an <h1> element. If it works for you as well, the problem may lie with the plugin.

Upvotes: 2

doru
doru

Reputation: 9110

You cannot do that, because, as the error says, the Bottle class has no attribute jinja2_view. But thre's no problem if you use them both, each for its purpose: Bottle() to instantiate your app and jinja2_view to render template.

Upvotes: 1

Related Questions