I want to write custom template loader for my Django app which looks for a specific folder based on a key that is part of the request.
Let me get into more details to be clear. Assume that I will be getting a key on every request(which I populate using a middleware).
Example: request.key could be 'india' or 'usa' or 'uk'.
I want my template loader to look for the template "templates/<key>/<template.html>". So when I say {% include "home.html" %}, I want the template loader to load "templates/india/home.html" or "templates/usa/home.html" or "templates/uk/home.html" based on the request.
Is there a way to pass the request object to a custom template loader?
I've been searching for the same solution and, after a couple days of searching, decided to use threading.local(). Simply make the request object global for the duration of the HTTP request processing! Commence rotten tomato throwing from the gallery.
Let me explain:
As of Django 1.8 (according to the development version docs) the "dirs" argument for all template finding functions will be deprecated. (ref)
This means that there are no arguments passed into a custom template loader other than the template name being requested and the list of template directories. If you want to access paramters in the request URL (or even the session information) you'll have to "reach out" into some other storage mechanism.
import threading
_local = threading.local()
class CustomMiddleware:
def process_request(self, request):
_local.request = request
def load_template_source(template_name, template_dirs=None):
if _local.request:
# Get the request URL and work your magic here!
pass
In my case it wasn't the request object (directly) I was after but rather what site (I'm developing a SaaS solution) the template should be rendered for.
To find the template to render Django uses the get_template method which only gets the template_name and optional dirs argument. So you cannot really pass the request there.
However, if you customize your render_to_response function to pass along a dirs argument you should be able to do it.
For example (assuming you are using a RequestContext as most people would):
from django import shortcuts
from django.conf import settings
def render_to_response(template_name, dictionary=None, context_instance=None, content_type=None, dirs):
assert context_instance, 'This method requires a `RequestContext` instance to function'
if not dirs:
dirs = []
dirs.append(os.path.join(settings.BASE_TEMPLATE_DIR, context_instance['request'].key)
return shortcuts.render_to_response(template_name, dictionary, context_instance, content_type, dirs)
Related
Suppose, we have a url path:
path('something/<int:some_param>/test/', views.some_view)
When a user hits this url, django makes an instance of HttpRequest, that will be passed to the some_view view. Is there a way to get the some_param url parameter from the request object outside the some_view code? (for instance, to do some processing that depends on some_param in a custom middleware).
One possible solution is to parse the HttpRequest.path attribute, but is there any prebuilt way to do this?
Django calls your view with the request object and some_param, which you have access to inside views.some_view. Your function will have the following signature:
def some_view(request, some_param):
....
Or:
def some_view(request, **kwargs):
some_param=kwargs.get('some_param', None)
You can then use some_param inside your view to do whatever you need to do with it, save it in a database, put it in a cookie, do calculations with it, get some database data with it, etc. Then once you're done, you need to return a response object. Usually by calling render or TemplateResponse with a template or returning HttpResponse without a template. You render templates providing a context dictionary which you are free to put anything you like into (like some_param), which makes it available to be rendered in your HTML template. That rendered HTML template is then returned as response to your user through the magic of the render function or TemplateResponse class, which ends the view process. I.e. like so:
return TemplateResponse(request, 'template.html', context)
To store some_param in between views, you'll need to save it in the database, store it in the user's session, or use a cookie. Or pass it to the next view inside the url or outside the url via /?param=some_param. Without saying what you need some_param for later on, it's hard to solve your issue.
The one possible solution here is to use the resolve function from django.urls module. It is extremely uselful if you want to access the URL parameters from URL path that is related to a HttpRequest object outside a view function. For example, get the URL params and process them in the custom middleware or other parts of your code.
Example:
from django.urls import resolve
...
func, args, kwargs = resolve(some_request.path)
I have a url that is meant to be accessed like
/people/raj/updates
/people/raj/updates?tag=food
But Django reverse URL resolver seems to have no provision to do tag=food, that is to detect it as an extra parameter and put in the query string.
How do I pass query parameters?
It depends on whether you are building the URL in the python code or in a template.
In python code (e.g. the view):
from django.http import QueryDict
query_dictionary = QueryDict('', mutable=True)
query_dictionary.update(
{
'tag': 'food'
}
)
url = '{base_url}?{querystring}'.format(
base_url=reverse(my.url.name),
querystring=query_dictionary.urlencode()
)
And in a template:
My Link
You caould also pass the QueryDict object from the view to the template and use that when building the URL in the template:
My Link
Django's reverse does not include GET or POST parameters. They are not part of the url.
You can of course always create the url by, for instance in a template, attaching the parameter as in:
{% url 'named_url' %}?tag=food
This way it gets attached anyway. Alternative is building an url regex that includes the possible tag, like:
url(r'^/people/raj/updates/(?P<tag>[a-zA-Z0-9]+/)?', yourview())
This way you can check for the kwarg tag in your view.
I couldn't find anywhere an explicit guide that will show what is the correct way. For example, I am using a package django-two-factor-auth for my django website. I wanted to add a context variable to the template and display it. I created a folder two_factor inside of my templates folder and the template file with the same name. That part is easy. But I also needed to inherit generic view to add my context (of course, I don't want to change source code of the third-party package). For this I created a new app inside my project and called it two_factor_custom and added following code to views.py:
from binascii import unhexlify
from base64 import b32encode
from two_factor.views.core import SetupView
class SetupViewCustom(SetupView):
def get_context_data(self, form, **kwargs):
context = super(SetupViewCustom, self).get_context_data(form, **kwargs)
if self.steps.current == 'generator':
key = unhexlify(self.get_key('generator').encode('ascii'))
context.update({
'secret': b32encode(key).decode('ascii')
})
return context
I would appreciate if you could say that this is the correct way to extend or override some of the behaviour of third-party packages. If not what I am doing wrong?
You can extend the template context by adding your own template context processor. See also the Django documentation.
The TEMPLATE_CONTEXT_PROCESSORS setting is a tuple of callables – called context processors – that take a request object as their argument and return a dictionary of items to be merged into the context.
i would like to know how can we "call" an application from inside a different application.
Basically, i have, for example :
a Calendar app that does a certain process to render a calendar in html
a "Main" app that shows the index.html of a website
I would like the Main app to invoke the Calendar app and insert what it renders in the sidebar.
The CodeIgniter framework for example, can handle that. A controller can invoke another controller, saves what it returns (the html output) in a variable, and finally includes this variable in the context that will be used to render the final html file.
Is that possible with Django ?
Thanks
Well, i think i may found a solution. I'm new to Django so i don't know if it's a good way to do it, if it brokes some conventional rules, if it open some security hole, or if simply, there are other better methods, but anyway, it works ...
So, i created my application Calendar, and my application Show.
I want Show to invoke Calendar, render its template, and then insert the result inside a template of Show.
To do that i used TemplateResponse instead of HttpResponse on the Calendar side :
# Calendar view
from django.template.response import TemplateResponse
def render_calendar(request):
return TemplateResponse(request, 'calendar/calendar-template.html', {})
Then on the Show side, get the TemplateResponse instance, invoke its render() method, and finally insert the rendered_content inside the context :
# Show view
from calendar import views
def show(request, show_id):
cal = views.render_calendar(request)
cal.render()
context = {"calendar": cal.rendered_content}
return render_to_response("show/show-template.html", context)
And that does the trick !
Sort of...
There is a template tag {% ssi %} that allows you to include one template inside another, the only thing is you will need to pass the parent template all the context variables needed in the other template.
I have been trying to use the updated yourlabs subscription example, and the installation has worked fine except that
Each of the views in the subscription app returns the request variable in the render to template.
this request context variable is not received in the template and as a result I am getting the following error
Caught VariableDoesNotExist while rendering: Failed lookup for key [request] in u
Since this is being returned in every view I can't solve this problem by making some tweaks in a particular template
This is happening because request isn't in your template's context and the template is using some template code that expected it to be there. That code (e.g. a custom template tag) should better handle VariableDoesNotExist
In addition, your views probably shouldn't return request in every response explicitly. Let Django handle this for you.
To do this, add the request template context processor that ships with Django to your TEMPLATE_CONTEXT_PROCESSORS:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'django.core.context_processors.request',
...
)
If you are already using this template context processor, ensure that render_to_response is called with context_instance=RequestContext(request) as the final argument (the below example is from the docs):
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
This ensures that all the dicts returned by the template context processors in TEMPLATE_CONTEXT_PROCESSORS are passed to the template.
You could also use the rendershortcut, which will automatically render the template with an instance of Requestcontext.
Another option added in Django 1.3 is the TemplateResponse, which will also use an instance of RequestContext.