How to dynamically select template directory to be used in flask?

PythonFlaskJinja2

Python Problem Overview


By default flask uses template files stored in "template" directory :

/flaskapp
    /application.py
    /templates
        /hello.html

Is there any way to dynamically choose template directory according to user logged in? This is how I want the directory structure to be :

/flaskapp
    /application.py
    /templates (default template goes here)
        /hello.html
    /userdata
        /user1
            /template1
                 hello.html
            /template2
                 hello.html
        /user2
            /template1
                 hello.html
            /template2
                 hello.html

Now if I have the username of logged in user and the name of template activated by user, is it possible to dynamically select the directory to load template files? For example,

/userdata/<username>/<activated template name>/

instead of fixed

/templates/

What I am trying to achieve is a wordpress like theme system for my web application where users can upload/select themes for his website.

Python Solutions


Solution 1 - Python

You can pass the Flask constructor a "template_folder" argument.

Like so...

Flask(__name__, template_folder="wherever")

Here's the documentation: http://flask.pocoo.org/docs/api/

Solution 2 - Python

There is also the possibility to overwrite Jinja loader and set the paths where Jinja will look for the templates. Like:

my_loader = jinja2.ChoiceLoader([
        app.jinja_loader,
        jinja2.FileSystemLoader(['/flaskapp/userdata', 
                                 '/flaskapp/templates']),
    ])
app.jinja_loader = my_loader

Directories are arranged in the order where Jinja needs to first start looking for it. Then from the view you can render user specific template like this:

render_template('%s/template1/hello.html' % username)

where username you can dinamically change in the view. Of course you can also there choose which template (1 or 2) to render. But basically what you really miss is this custom Jinja loader with the custom paths.

Hope that helped or gave the ideas :)

Solution 3 - Python

I'm new to Python, but I have already faced with this problem. I don't know if my solution is right, but it works:

First of all you have to create module for each user

/flaskapp
    /application.py
    /templates (default template goes here)
		__init__.py 	# default module flaskapp
		views.py 		# here you can define methods for default module (like Action in MVC)
        /hello.html
	/static
    /userdata
        /user1
			__init__.py # user1 module
			views.py 	# here you can define methods for user1 module
            /template1
                 hello.html
            /template2
                 hello.html
        /user2
			__init__.py # user2 module
			views.py 	# here you can define methods for user2 module
            /template1
                 hello.html
            /template2
                 hello.html				 
				 

in application.py init Flask app, add global method render_page_from and register blueprints

app = Flask(__name__)
def render_page_from(controller_name, template_name_or_list, **context):
	# here you can choose any controller or use default
    app.jinja_loader.searchpath.clear()
    blueprint = app.blueprints[controller_name]
    app.jinja_loader.searchpath.append(blueprint.template_folder)
    return render_template(template_name_or_list, context=context)
	
from flaskapp.user1 import controller as user1_controller
from flaskapp.user2 import controller as user2_controller

app.register_blueprint(user1_controller)
app.register_blueprint(user2_controller)

in each module (user1, user2 etc) init blueprint in init.py

templates_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')

controller = Blueprint('user1', __name__, url_prefix='/user1', template_folder = templates_folder)

import flaskapp.user1.views

finally add view (action) methods to views.py like this

from LocalHub.client import controller
@controller.route('/hello')
def hello():
    """Renders the page"""
    return render_page_from(controller.name, 'hello.html', title='hello')

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanujkkView Question on Stackoverflow
Solution 1 - PythonnathanView Answer on Stackoverflow
Solution 2 - PythonIgnas ButėnasView Answer on Stackoverflow
Solution 3 - PythonEgorView Answer on Stackoverflow