I am using bottle.py framework along with Jinja2 templates in a new application.
Whenever a user logs into the application I would like to add a new global Jinja2 variable with the name of the active user, so in a partial template (the header) I can display this name.
In my python (bottle) file I've tried several things according to what I googled so far, but no success yet.
This is the last thing I tried inside a function:
import jinja2
env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'))
env.globals['myglobal'] = 'My global'
#env.globals.update({'myglobal': 'My global'})
But, when putting this into the header template: {{myglobal}} it simply doesn't show up.
Since this is my first time with bottle and jinja2 anyone knows how can achieve this?
I have a similar setup, and this works for me using the BaseTemplate built into bottle:
from bottle import BaseTemplate
BaseTemplate.defaults['myglobal'] = 'My global'
In this case you need to use before_request hook, just like below:
from bottle import Jinja2Template
#hook('before_request')
def before_request():
Jinja2Template.defaults['myglobal'] = 'My global'
from bottle import Jinja2Template
Jinja2Template.defaults['foo'] = 'bar'
This is to add a variable to every rendered jinja2 templates. If you want to add a filter, here is what I did:
import functools
from bottle import jinja2_view
def foo():
return 'bar'
view = functools.partial(jinja2_view, template_settings={
'filters': {'foo': foo}})
You can set any jinja2 settings using this.
Related
I am using flask and trying to the following.
I have defined a main.py file through which I want to run my app ie python main.py -
from flask import Flask
from view import tags
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
I have defined a package named view in which I will be declaring my different view modules, each having its own routes. view.tags.py -
from flask import Flask
app = Flask(__name__)
#app.route('/e')
def hello_world2():
return 'Hello World!'
So I need to have the global app object in my main.py for running the server, as well as in the view classes of my package for registering the routes. So how do I create the global app object and share it between all classes ?
Thanks,
Murtaza
You can import current_app from flask. It stores a reference to the global application object.
from flask import current_app as app
def home():
return render_template('base.html', name=app.name)
First, I would suggest to take a look at Blueprints http://flask.pocoo.org/docs/blueprints/ This will help to organize the app easily.
Also take a look at http://flask.pocoo.org/docs/api/#flask.current_app flask.current_app, the way how to get your app instance in other modules.
This link also could be helpful on how to organize and build flask app (it is not ideal for sure, but can give you some ideas) - Large-app-how-to.md
Have fun :)
One way is to create an overall package and adding a __init__.py file under that where you declare all global variables. In your case for example, you can create something like:
myapplication/
* __init__.py
* myviews/
* __init__.py
* view.py
* tags.py
etc
Now you add the following code in the __init__.py file:
app = Flask(__name__)
You can now use this app variable anywhere as long as you import the package myapplication.
import myapplication.myviews.view
Just import it from your other files. Perhaps the best way to do this is to put your app object in one single file and have everything else import from it.
For example, your main.py could still have:
from flask import Flask
from view import tags
app = Flask(__name__)
And then in other files, you could do:
from .main import app
or, if outside your package, just use the complete string
from mypackagename.main import app
One thing to be careful of is circular imports. The easiest way to handle this issue is to create your app first and then import whatever else you need to from your base file after you create it.
So for example:
from flask import Flask
app = Flask(__name__)
# do some stuff with app
from .views import view1, view2
from .server import run
So long as you put the imports after you've created app, like the above, you shouldn't have an issue with circular imports.
Regarding import and use of current_app from flask in a "helper" python function in a separate source file, this works as long as a current app context has already been set up (E.g. web request received). I have a case where, during application initialization (app.run not yet invoked), app.logger is invoked in the helper function.
Before I fixed it (see below), I got a stack trace punctuated by "RuntimeError: Working outside of application context".
Sample solution:
main.py:
import helper
...
app = Flask(__name__.split('.')[0],
template_folder="templates",
static_folder="static")
...
# Fix: Create an app context
with app.app_context():
helper.dbopen(...)
...
app.run(...)
helper.py:
from flask import current_app as app
...
def dbopen():
app.logger.info(...)
...
If you have a file AppName.py in which you define app, and then you have another file Foobar.py that needs it, you can always say in AppName.py:
import Foobar
Foobar.app = app
Then in Foobar.py you should be able to use app in your functions. One thing you want to be careful of is that you can't have code in Foobar.py that runs immediately when the file is called the depends on the app variable which is passed in after the import.
I have a series of blueprints I'm using, and I want to be able to bundle them further into a package I can use as seamlessly as possible with any number of other applications. A bundle of blueprints that provides an entire engine to an application. I sort of created my own solution, but it is manual and requires too much effort to be effective. It doesn't seem like an extension, and it is more than one blueprint(several that provide a common functionality).
Is this done? How?
(Application dispatching methods of tying together several programs might work isn't what I'm looking for)
Check this out: Nesting Blueprints → https://flask.palletsprojects.com/en/2.0.x/blueprints/#nesting-blueprints
parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)
I wish the Blueprint object has a register_blueprint function just as the Flask object does. It would automatically place and registered blueprints under the current Blueprints' url.
The simplest way would be to create a function that takes an instance of a Flask application and registers all your blueprints on it in one go. Something like this:
# sub_site/__init__.py
from .sub_page1 import bp as sb1bp
from .sub_page2 import bp as sb2bp
# ... etc. ...
def register_sub_site(app, url_prefix="/sub-site"):
app.register_blueprint(sb1bp, url_prefix=url_prefix)
app.register_blueprint(sb2bp, url_prefix=url_prefix)
# ... etc. ...
# sub_site/sub_page1.py
from flask import Blueprint
bp = Blueprint("sub_page1", __name__)
#bp.route("/")
def sub_page1_index():
pass
Alternately, you could use something like HipPocket's autoload function (full disclosure: I wrote HipPocket) to simplify the import handling:
# sub_site/__init__.py
from hip_pocket.tasks import autoload
def register_sub_site(app,
url_prefix="/sub-site",
base_import_name="sub_site"):
autoload(app, base_import_name, blueprint_name="bp")
However, as it currently stands you couldn't use the same structure as example #1 (HipPocket assumes you are using packages for each Blueprint). Instead, your layout would look like this:
# sub_site/sub_page1/__init__.py
# This space intentionally left blank
# sub_site/sub_page1/routes.py
from flask import Blueprint
bp = Blueprint("sub_page1", __name__)
#bp.route("/")
def sub_page1_index():
pass
I have solution for myself how to load blueprints defined in configuration, so then you can have something like CORE_APPS = ('core', 'admin', 'smth') in config and when you construct app you can register those apps (of course those strings in CORE_APPS must be the names of the files you want to import in your python path).
So I'm using function to create app:
app = create_app()
def create_app():
app = Flask(__name__)
# I have class for my configs so configuring from object
app.config.from_object('configsClass')
# does a lot of different stuff but the main thing could help you:
from werkzeug.utils import import_string
for app in app.config['CORE_APPS']
real_app = import_string(app)
app.register_blueprint(real_app)
After that your blueprint should be registered. Of course you can have different format in configs to support custom url prefixes and so on and so on :)
Of course you can also do something like this in your main blueprint, so in the application creation you will need to register that one main blueprint.
I'm using Jinja2 templates with Bottle.py and Google App Engine's dev_appserver for development. I want the templates to automatically reload on every request (or ideally only when they change), so that I don't have to keep restarting the server.
According to bottle's docs, you're supposed to be able to disable template caching by calling bottle.debug(True).
Jinja still seems to be caching its templates, though. I believe this to be because of the way the bottle jinja2 adapter is written (it just uses a default Jinja2 loader and doesn't expose many configuration options).
Following the Jinja2 Docs, I wrote this custom Loader that I would expect to trigger a template reload every time, but it doesn't seem to work either:
import settings
from bottle import jinja2_template
from bottle import Jinja2Template, template as base_template
class AutoreloadJinja2Template(Jinja2Template):
def loader(self, name):
def uptodate():
# Always reload the template if we're in DEVMODE (a boolean flag)
return not settings.DEVMODE
fname = self.search(name, self.lookup)
if fname:
with open(fname, "rb") as f:
source = f.read().decode(self.encoding)
return (source, fname, uptodate)
template = functools.partial(base_template,
template_adapter=AutoreloadJinja2Template,
template_lookup = settings.TEMPLATE_PATHS,
template_settings={
'auto_reload': settings.DEVMODE
}
)
Templates are still getting cached until I restart dev_appserver. This must be a fairly common problem. Does anyone have a solution that works?
UPDATE:
I ended up doing something like:
class CustomJinja2Template(Jinja2Template):
if settings.DEVMODE:
def prepare(self, *args, **kwargs):
kwargs.update({'cache_size':0})
return Jinja2Template.prepare(self, *args, **kwargs)
template = functools.partial(original_template, template_adapter=CustomJinja2Template)
This causes the templates to always reload, but only if a python module has been touched. i.e. if you just edit a template file, the changes won't take affect until you edit one of the python files that imports it. It seems as if the templates are still being cached somewhere.
I resolved this issue by ditching bottle's template solutions completely and using pure jinja2. It seems that Jijnja's FileSystemLoader is the only one, which can watch for file changes.
I defined new template function as follows (it looks for files in views/, just like bottle used to):
from jinja2 import Environment, FileSystemLoader
if local_settings.DEBUG:
jinja2_env = Environment(loader=FileSystemLoader('views/'), cache_size=0)
else:
jinja2_env = Environment(loader=FileSystemLoader('views/'))
def template(name, ctx):
t = jinja2_env.get_template(name)
return t.render(**ctx)
Then I use it like this:
#route('/hello')
def hello():
return template('index.tpl', {'text': "hello"})
The difference from bottle's API is that you have to include .tpl in file name and you have to pass context variables as dictionary.
Bottle caches templates internally (independent from Jinja2 caching). You can disable the cache via bottle.debug(True) or bottle.run(..., debug=True) or clear the cache with bottle.TEMPLATES.clear().
The Environment object in jinja2 has a configuration value for the cache size and, according to the documentation,
If the cache size is set to 0 templates are recompiled all the time
Have you tried something like this?
from jinja2 import Environment
env = Environment(cache_size=0)
Using bottle view decorator, you can just do #view('your_view', cache_size=0).
Bottle has a reloader=True parameter in server adapter, but I guess it works only with SimpleTemplate. I will try to extend this behaviour to other template engines.
If you want to do it in all your views, maybe you can do something like this:
import functools
view = functools.partials(view, cache_size=0)
This way, you can do it only when you are in debug mode adding an if statement to this code if bottle.DEBUG.
I'd like to add filter to format my time and the best would be filters like django's timesince that automatically outputs the language of the i18n selected language, but first to make a quick solution I'd like to format my date. The suggested solution from the manual is:
def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
return value.strftime(format)
jinja_environment.filters['datetimeformat'] = datetimeformat
But adding this code to my file doesn't make the filter available in the template:
{{ ad.modified|datetimeformat }}
TemplateAssertionError: no filter named 'datetimeformat'
If I add the code to the Jinja2 library's filters.py then it works. But I shouldn't need to add to Jinja2 files manually, it should work just adding the Jinja2 to my app.yaml and put my filter in my code instead of in the Jinja2 code. Where should I put the filter code?
Thank you
Update
My code looks like this and it seems the filter is not picked up:
from django.utils import translation
from django.utils.translation import gettext, ngettext, ugettext, ungettext, get_language, activate
from jinja2 import Environment, FileSystemLoader
class DjangoTranslator(object):
def __init__(self):
self.gettext = gettext
self.ngettext = ngettext
self.ugettext = ugettext
self.ungettext = ungettext
class DjangoEnvironment(jinja2.Environment):
def get_translator(self, context):
return DjangoTranslator()
jinja_environment = DjangoEnvironment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), extensions=['jinja2.ext.i18n'])
jinja_environment.install_gettext_translations(translation)
def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
return value.strftime(format)
jinja_environment.filters['datetimeformat'] = datetimeformat
Following your example and Jinja2 docs I've added custom filter and it works.
Make sure that you use proper jinja2.Environment instance for getting template and rendering:
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_path))
env.filters['default_if_none'] = default_if_none # a function
tmpl = env.get_template(filename)
tmpl.render(**context)
Because I was using a cached jinja2 environment as recommended here,
Kee's answer didn't work for me, but this one did.
Specifically, adding the filter when calling webapp2.WSGIApplication
myconfig = {}
myconfig['webapp2_extras.jinja2'] = {'template_path': ['templates','blog_posts'],
'filters': {'blog_filter': blog_filter}}
app = webapp2.WSGIApplication(_routes,
debug=True,
config = myconfig)
I am working on a cherrypy+cheetah app and would like to improve the development experience.
I have everything working when I manually compile templates beforehand. (Update: This is how things work for production: precompile, don't ship *.tmpl and load templates as regular python modules.) However, during development I'd rather just load the templates every time they are referenced so that I don't need to kill and restart my application. I have a couple of issues I am facing:
If I have templates inheriting from base templates, I get import errors (can't find base templates). I think I had this actually working during my experiments, but unfortunately didn't save it and now I can't make it work.
Suppose I get 1. working, how do make it so that edits even in base templates get picked up without restart.
Below is my sample application that should demonstrate the problems. The directory structure is as follows:
t.py
templates/
base.tmpl
index.tmpl
t.py:
import sys
import cherrypy
from Cheetah.Template import Template
class T:
def __init__(self, foo):
self.foo = foo
#cherrypy.expose
def index(self):
return Template(file='templates/index.tmpl',
searchList=[{'foo': self.foo}]).respond()
cherrypy.quickstart(T(sys.argv[1]))
base.tmpl:
#def body
This is the body from the base
#end def
This is the base doc
index.tmpl:
#from templates.base import base
#extends base
#def body
$base.body(self)
This is the extended body
#end def
This is from index
Run it like this:
python t.py Something
Try this:
Replace base.tmpl with:
#from Cheetah.Template import Template
#def body
#set $base = Template(file="templates/base.tmpl")
$base.body()
<br/>
This is the extended body
#end def
$body()
<br/>
This is from index
Looks like this question was kind of answered in another SO question. By using the cheetah_import function, I can write my methods like this:
#cherrypy.expose
def index(self):
templates = cheetah_import('templates.index')
t = getattr(getattr(templates, 'index'), 'index')(searchList=[{'foo': self.foo}])
return t.respond()
I will also need to add an __init__.py into the templates directory. This approach also requires that Cherrypy's engine.autoreload_on setting is set to True.
To make production more efficient, I can make cheetah_import = __import__ and not replace the default __builtin__.import.