I've written a common blueprint for a bunch of independent little apps as a python package. Mostly this is just a top-level template and a style sheet to give the apps a unified design. I'd like to display the current app's name in the top-level template, but I have no idea how. It seems that there is no way to get to the app context from within the blueprint that the app registered.
I've tried this:
#blueprint.context_processor
def appinfo():
return { 'appname': flask.current_app.name }
but it only works when the template is rendered by one of the blueprint's view functions but not from the app's.
blueprint.app_context_processor() does the trick.
Using the Blueprint.context_processor decorator implies that the function will be called only for requests handled by the blueprint. Namespaces are great!
If you'd like 'appname' to be available in every template: app and blueprints alike, registering the context processor with Flask.context_processor should satisfy that.
app = Flask(__name__)
#app.context_processor
def appinfo():
return dict(appname=app.name)
Related
I'm making a Flask app. In some views I need to work with LaTeX templates instead of HTML.
It is posibble to create another Jinja2 environment and use the render_template function choosing the environment to use?
I'm using blueprints and I want to assign the templates folder in same way that I use the main Jinja environment.
You can use Jinja's Environment.overlay method to copy Flask's environment settings and then insert your own template loader to find template specific to a given blueprint (even if they share the name of a template elsewhere in the application).
from jinja import ChoiceLoader
new_loader = ...
existing_loader = app.jinja_env.loader
new_environment = app.jinja_env.overlay(
loader=ChoiceLoader([new_loader, existing_loader]),
autoescape=select_autoescape(['html', 'xml'])
)
Here, I define a new template loader and insert it at the front of Jinja's lookup order. This retains all the rest of the Flask environment settings including variables like request and app.
Alternatively, you could wrap Flask's render_template() function and do the same thing. I've seen both approaches successfully deployed in production.
I am developing the mobile version of my website, so thought of using user-agent as the criteria for serving different templates for mobile and web version.
I successfully read the user-agent information from nginx and passed it as header to gunicorn server.
Then I created a middleware which reads this header and changes the templates directory in settings file. This seemed to work initially but then I realized that there is race condition happening as this method is not thread safe. (I should have thought of it before-hand).
So I started thinking of other alternatives. One solution was to overwrite the render method of django to include "dirs" parameter based on request header. But then I found out that the "dirs" parameter is deprecated. Following is the reference link https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render
So even this will not work.
Another solution is to have different template names for mobile and web and load them accordingly. However I don't want to do this and want to keep the templates directory structure exactly same for both web and mobile.
There has to be a way to just overwrite the template directory. This will give me an advantage of falling back on web version of templates if its missing in mobile templates directory.
Any advise on how to achieve this will be helpful.
This is how my templates are organized.
App1
templates
App1
index.html
catalog.html
App2
templates
App2
about.html
And in the project directory(not part of the app folder), there is a mobile templates folder which has the following structure
mobile-templates
App1
index.html
App2
about.html
Thanks
Anurag
Here's how I would organize my templates:
Make two directories inside templates dir - mobile and desktop.
Keep mobile templates in mobile dir and desktop templates in desktop.
This way you won't have to rename the templates.
And here's how I would render them:
Read User-Agent in a middleware.
Set an attribute on request called template_prefix whose value will either be mobile or desktop, depending on the User-Agent. Eg:
def process_request(self, request):
# read user agent and determine if
# request is from mobile or desktop
# ...
if mobile_user_agent:
request.template_prefix = 'mobile'
else:
request.template_prefix = 'desktop'
In your views, use request.template_prefix before template names. Eg:
def home(request):
...
template = request.template_prefix + '/home.html'
return render(request, template)
This will render the templates from either mobile or desktop dirs depending on the value template_prefix attribute.
UPDATE (according to question edit):
Looking at how your templates are organized, I'd do this:
Middleware:
Only set template_prefix for mobile requests.
def process_request(self, request):
if mobile_user_agent:
request.template_prefix = 'mobile-templates'
else:
request.template_prefix = '' # set an empty string
Views:
Use os.path.join; catch TemplateDoesNotExist exception.
import os.path
from django.template.loader import get_template
from django.template.base import TemplateDoesNotExist
def index(request):
try:
template = os.path.join(request.template_prefix, 'App1/index.html')
get_template(template)
except TemplateDoesNotExist:
template = 'App1/index.html'
return render(request, template)
I've tested this and it works. But writing a try...except block in every view seems redundant. If I come up with a better solution, I will update.
It seems it isn't possible to this out of the box right now. If you really want to follow this architecture, you will have to write your own custom loader and also figure out a way to pass the request/indicator to let it know its a mobile request.
It's not too tough to write the loader (just see the Django filesystem loader, if the request is from mobile, loop through all the templates_dirs and add the proper suffix to it, so that you include mobile dirs too).
However the biggest challenge as I see it is being able to pass a dynamic parameter to it (indicating that this is a mobile request). You may store this param in the session or modify the template name before passing it to the custom renderer (The renderer will remove this indicator part and get the template) .
In the django tutorial, it recommends that an application's templates go in
project/app_name/templates/app_name
which results in them being referred to from views something like this:
class ScenarioDetails(generic.DetailView):
model = Scenario
template_name= "cmmods/scenario.html"
I would like to understand what is behind this recommendation.
I can't see what situation would make it bad to put templates in
project/app_name/templates
and refer to them thusly:
class ScenarioDetails(generic.DetailView):
model = Scenario
template_name= "scenario.html"
What is behind this recommendation?
It avoids name clashes and it explicitly refers to the app where the template is located.
Say your app defines a couple of templates that are very similar. An easy way to achieve this is to use a base template, e.g. 'base.html'. This is quite a common name, and it is quite likely that other apps use the same name. If you put this template in your root template folder, the names clash and both apps get the same template, depending on which one comes first (or last) in your INSTALLED_APPS.
By putting each template in a folder named after your app, you avoid these name clashes. It is also easier to locate the template from just the template name, which is very useful if your app uses templates from other apps.
Say you use the template detail.html in your app2 app. Django will look for this template in all template folders in the order they are found. Now, your installed apps looks like this:
INSTALLED_APPS = (
'app1',
'app2',
)
Django will look for the detail.html file in the following locations, and picks the first one it finds:
/path/to/project/app1/templates/detail.html
/path/to/project/app2/templates/detail.html
Even though app2 expects the template file in app2/templates/, Django will give the template in app1/templates/ if it exists. You get the wrong template because the (relative) names are the same.
Now, if you would put your template in app2/templates/app2/detail.html, you are sure you get the right template.
The reason is that templates need to be kept separate from templates of other Django apps to prevent collision. This is why all templates in Django apps should be saved in a directory called app_name/templates/app_name.
For example, without doing this, two apps with the same template (same filename) would have a problem if they're used together in the same Django project (without additional work, Django wouldn't know which of these home.html files to use):
app1/templates/home.html
app2/templates/home.html
As a result of the convention to include the app name in the templates path, you can safely create and release a Django app and reuse it in other Django projects without fearing that there will be a filename problem.
I have a bunch of variables that need to be available to the view for all templates. It seems the best choice would be a context processor.
The documentation says:
A context processor has a very simple interface: It’s just a Python
function that takes one argument, an HttpRequest object, and returns a
dictionary that gets added to the template context. Each context
processor must return a dictionary.
If I need to do more advanced lookups, can I define other functions? Do the functions need to be in a class? I was thinking of creating a file named context_processors.py in my app folder.
You can define other functions, and the functions don't need to be in a class.
Typically people put their context processors into a context_processors.py like you're thinking of as functions, and then name them all in settings.TEMPLATE_CONTEXT_PROCESSORS.
For example, here's an app that has the context_processors.py inside it: django-seo.
I have a bunch of functions that I created in some views that must be reused in many other views. Do I need to create a class and put those functions in a class? If yes how exactly has to be done in Django and then how do I call and initiate them in the new views?
Django views are just Python functions. You can call other Python functions from them just as you can from any other Python code. Put your functions into a .py file, import it, and invoke the functions.
Of course, it may make sense for other reasons to create a class to hold the functions, but you certainly don't need to in order to call them from views.
The solution would be to create the myfunctions.py file in your app folder and import it in your views. Your views file would look like:
import myfunctions
def my_view(request):
.....
foo = myfunctions.bar()
....
You look to complicated to Django. Django is just another Python application and lives in the Python world. For example you can create file library.py and import this file where is needed (in modules where the function from library is needed). If you need you library's functions to get data from database, just import models in the library.py and use them.
Django doesn't have such a big think as "module". You creating the architecture, you can define what in your case is module. In general this is just simple directory with init.py file inside to be able to import files from there.
Hope that helped.
From my point of view, if you have a lot of functions that are widely used in your project it make sense put all this in some separate application. I create some module named 'contrib' for this purposes. It can avoid some time for maintaining this code in future