I have a custom context processor that passes some server side constants to the client (by injecting into javascript object). Some views might want to add additional params, notifications for example, to the dict.
from django.contrib.sites.shortcuts import get_current_site
def client_context(request):
params = {}
domain = get_current_site(request).domain
schema = request.scheme
params['rooturl'] = "%s://%s" % (schema,domain)
return {'params': params}
According to the docs
When context processors are applied
Context processors are applied after the context itself is processed. This means that a context processor may overwrite variables you’ve supplied to your Context or
RequestContext, so take care to avoid variable names that overlap with
those supplied by your context processors.
If I render a template without overriding params it's working fine - i get the baseurl in the template.
If I render the template with a variable named params i get only the overrided value.
def home(request):
return render(request, 'home.html', { 'params' : {'page' : 'home'} })
so i have two questions
Is the django docs incorrect ? from what i understand the second
dict should update the first one. According to these findings my
custom context is applied first.
What is the best way to achieve this ?
EDIT
In my base.html file (extended by all other templates) i pass the params object to a Js objects. e.g
{% block init %}
<script>
Global = new Global( {{ params | json }} );
</script>
{% endblock %}
I was hoping for a solution that wont require adding each possible parameter by name
{% block init %}
<script>
Global = new Global( { baseurl : "{{baseurl}}", page : "{{ page }}" );
</script>
{% endblock %}
Django is working as documented. The params returned by your template context processor is overwriting the params in the view's context. Django doesn't automatically merge dictionaries as you want.
Instead of using the render shortcut, you could make your views return TemplateResponse objects. Then, instead of writing a context processor, write a middleware that updates the params dict in the process_template_response method.
Related
What is the recommended way to write views (as functions) in Django?
I am asking in terms of readability, etc.
For example: define the template first, then do the translations, then define models and lastly define context.
Here is some example code:
def index(request): # Landing page, translated to the browser's language (default is english)
template = loader.get_template("koncerti/index.html")
# Translators: This is the text on the homepage buttons
concerts = gettext("Koncerti")
band = gettext("Band")
# Translators: This is the option in the language-switch box
foreignLanguage = gettext("eng")
koncertiUrl = '/koncerti/international' # The URL slug leading to 'koncerti' page
bandUrl = '/band/international' # The URL slug leading to 'band' page
translationMode = '' # The URL slug that is leading to slovenian translation mode
context = {
'Concerts' : concerts,
'Band' : band,
'foreignLanguage' : foreignLanguage,
'koncertiUrl' : koncertiUrl,
'bandUrl' : bandUrl,
'translationMode' : translationMode
}
return HttpResponse(template.render(context, request))
I think you are doing too much in the view. A view is supposed to make the proper querysets, and make changes to the database. It's main task is thus to decide what to render, and what to update. Not how to update it.
Deciding about translations, url resolution, etc. is normally a task of the template. The template has template tags for that such as {% trans … %} [Django-doc] and {% url … %} [Django-doc].
Using the {% url … %} over raw URLs you construct yourself is strongly advisable. It makes the odds of making a mistake smaller, and furthermore it is clear to what view you are referring.
You furthermore might want to use render(..) [Django-doc]. This function combines looking for a template, rendering that template with the template render engine, and returning a HTTP response.
from django.shortcuts import render
def index(request):
return render(request, 'koncerti/index.html', {})
In the template, you can then render this with:
{% load i18n %}
{% trans 'Koncerti' %}
With the following renderer...
from django.shortcuts import render
...
return render(request, 'template.html', {'context':context,})
Is it possible to override the render classe's methods so that I can in certain circumstances interpret template tags myself for example if I find a tag consisting of a certain format such as...
{% url 'website' page.slug %}
I could point it to...
/theme/theme-1/page.html
or
/theme/theme-2/page.html
depending on extranious settings.
The render method is just a shortcut for:
template = loader.get_template(''template.html')
context = {
...,
}
return HttpResponse(template.render(context, request))
Therefore, it is not the correct place to try to change the behaviour of the url tag.
For the example you have given, it looks like the website should be a variable that holds theme-1 or theme-2. You can then pass the variable to the url tag instead of the string 'website'.
{% url website page.slug %}
If that is not possible, you could create a custom template tag my_url that returns the correct url depending on your settings.
Django says there's 3 ways to turn off autoescape:
Use |safe after the variable
Use {% autoescape on %} and {% endautoescape %} within blocks
Use a Context like context = Context({'message': message}, autoescape=False)
(1) and (2) work fine. But I have the situation where I have templates to generate plain-text push notifications, and I have LOADS of templates to build and maintain. I could go through and put the {% autoescape on %} and {% endautoescape %} tags in all of them, but (3) should allow me to do it in one line in the view.
The template:
{% block ios_message %}{{message}}{% endblock %}
The view:
message = u"'&<>"
context = Context({'message': message}, autoescape=False)
render_block_to_string(template_name, 'ios_message', context)
The output:
u''&<>
The code for block_render.py is from here: https://github.com/uniphil/Django-Block-Render/blob/master/block_render.py. I'm using it as is from there.
Anyone know what gives?
Take a closer look to function render_block_to_string():
def render_block_to_string(template_name, block, dictionary=None,
context_instance=None):
"""Return a string
Loads the given template_name and renders the given block with the
given dictionary as context.
"""
dictionary = dictionary or {}
t = _get_template(template_name)
if context_instance:
context_instance.update(dictionary)
else:
context_instance = Context(dictionary)
return render_template_block(t, block, context_instance)
The 3rd arg should be a dict, not context. Otherwise it would use the normal context instance.
So I believe it should be:
render_block_to_string(template_name, 'ios_message', {}, context)
Hope it helps.
I could solve it my doing it like that:
from django.template.context import make_context
from django.template.loader import get_template
# Getting the template either by a path or creating the Template object yourself
template = get_template('your/path/to/the/template.html')
# Note here the 'template.template' and the 'autoescape=False' parameter
subject = template.template.render(make_context(context, autoescape=False))
Found it by doing that myself. Because by default the autoescape setting will be used from the engine
https://github.com/django/django/blob/4b6dfe16226a81fea464ac5f77942f4d6ba266e8/django/template/backends/django.py#L58-L63
Django Version: 2.2.1
Wanted to comment but looks like I don't have enough reputation since this is a newish account
You can turn off the autoescape found here: https://github.com/django/django/blob/529c3f264d99fff0129cb6afbe4be2eb11d8a501/django/template/context.py#L137
i.e.
Context(data_dict, autoescape=False)
I am using inclusion_tags to generate portions of my pages that repeat in many different places across my site.
templatetags/tags.py
#register.inclusion_tag('chunk_template.html')
def output_chunk(object, edit):
... # Lots of set up work here that I don't want to duplicate in the view
return { ... }
Upon AJAX submit of a form on the page, I need to refresh the very same HTML outputted by output_chunk(). To avoid completely rewriting output_chunk() in a view, I did the following as recommended in this answer about how to use templatetags in views:
views.py
def chunk(request, ...):
context = Context({..., 'request': request })
template_string = """
{% load output_chunk from tags %}
{% output_chunk ... %}
"""
t = Template(template_string)
return HttpResponse(t.render(context))
This is all working fine, except chunk_template.html calls {% csrf %}, which works when I call the template tag the standard way, but not when I call in this somewhat hacky way (to avoid writing the same code twice).
(For simpler template tags, this works fine when I call return render (request, template_name, context) from within the view.)
So, is there a better way to call the template tag from within the view to get all the middleware invoked properly? Or is there something I can add to this hack to make it work properly?
I don't understand the core of the problem, but you can always manually pull the token (the middleware calls this function).
from django.middleware.csrf import get_token
csrf = get_token(request)
Need to make the context a RequestContext.
context = RequestContext(request, {....})
{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.