yourlabs-subscription error -- Caught VariableDoesNotExist while rendering - django

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.

Related

Why does Django's render() function need the "request" argument?

Sorry for what might be a silly question, but why is the request argument mandatory in the render() function?
The render() shortcut renders templates with a request context. Template context processors take the request object and return a dictionary which is added to the context.
A common template context processor is the auth context processor, which takes the request object, and adds the logged-in user to the context.
If you don't need to render the template with a request context, you can use request=None.
def my_view(request):
return render(None, "my_template.html", {'foo': 'bar'})
For rendering a template outside of the context of a view (i.e. without a request object), one can use render_to_string():
from django.template.loader import render_to_string
render_to_string('path/to/template.html', context={'key': 'val'})
In django render is used for loading the templates.So for this we
import-from django.shortcuts import render
its a template shortcut.
Rendering is the process of gathering data (if any) and load the associated templates

Passing request to custom Django template loader

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)

Access request in django custom template tags, using the tag decorator

There is a question with the same title and problem: Access request in django custom template tags
The solution proposed, for instance pass takes_context = True to the decorator works perfectly well for tag helpers (simple_tag, inclusion_tag and assignment_tag) but not with the low-level tag decorator.
#register.tag(takes_context=True)
def mytag(parser, token):
...
raises the exception :
tag() got an unexpected keyword argument 'takes_context'
2 questions :
is there a reason why the tag decorator does accept the takes_context argument ?
how can I access the template context with this decorator (and thus the request object) ?
Answer to the 2 sub-questions:
Is there a reason why the tag decorator does accept the takes_context argument ?
It's because, as you said, tag is run at a lower level, parser-level of the template. There is no context at that point.
How can I access the template context with this decorator (and thus the request object) ?
I don't think you can. Again, because it's a parser-level thing. An interesting example is the block tag. In order to be able to override block tags, it uses the extend tag to pass on some info.
I'm struggling with similar situation. The only thing I can think of is making middleware for process_request that resets some global/singleton context which I access in tags. However this wont help if a template is rendered outside a request, like in a command.
#register.tag takes only two arguments: the name and the compilation function. I think you can try to pass request through the token argument in the compilation function.
UPD:
You can also access it in the Node render method
class TestTagNode(template.Node):
def render(self, context):
return context['request'].user
#register.tag
def test_tag(parser, token):
return TestTagNode()

Setting a variable in middleware to be accessed in the template

I seem to be having difficulty setting a variable in one of my middleware classes that I can then access in the template layer.
The basic layout is this:
class TheMiddleware(object):
def __init__(self, etc):
stuff...
def process_response(self, request, response):
request.my_var = "whatever"
return response
Then on the template for a different view I have:
{% custom_tag arg_a %}
Which is is a template tag that should return the variable from the request:
#register.simple_tag
def custom_tag(arg_a):
return threading.currentThread().request.my_var
This errors out with "Caught AttributeError while rendering: 'WSGIRequest' object has no attribute 'my_var'"
I thought it might be the way I was accessing the request in the template tag. So I added django.core.context_processors.request to my TEMPLATE_CONTEXT_PROCESSORS as in This question and tried passing the request object to the tag, then accessing request directly from the template but with no luck.
I think I lack an understanding on how request objects work. Is it possible to assign a variable to a request object and pick that variable up several views on? I thought the request object was passed through the views, but it seems that instead a new instance is generated.
If that is the case, how would you go about storing a global variable within middleware that you could then access from any point in your app, be it in a view or a template?
Update:
To clear up the confusion (whether mine or others I'm not sure!) I'm not trying to set the request variable in the process_response middleware of a view and then pick it up in the template of that same view. I understand that that wouldn't work because the template has been processed before the variable is saved. (This is a deliberate act on my part).
I have two views, view1 and view2 view one has a decorator that causes the middleware to set the variable in the request. It is the view2 template, which comes after the variable has been set, that I wish to access the variable.
You trying to set variable during processing of response in your middleware.
I think you should be implementing process_request() instead to set the variable.
def process_request(self, request):
request.my_var = "whatever"
return
If you're setting it on the request, I can't see any reason at all to try and use threadlocals here. You should use the context processor as you describe.
The reason for your problem, though, is that process_response is run in the response phase of the request/response cycle: ie, after your view has been called. You should define process_request instead. See here for a description of the order that middleware methods are called.

Accessing request.META.SERVER_NAME in template

I call a template like this from my view:
return render_to_response('mytemplate.html', context_instance=RequestContext(request))
I'm trying to access the hostname of my current server (in this case, localhost), but it just prints blank when I place {{request.META.SERVER_NAME}} in the template.
In my settings.py file, I don't have any TEMPLATE_CONTEXT_PROCESSORS defined. I'm not sure if I need to specify anything there, or if that could solve the problem.
You have to add the request context processor to have it added to the template context automatically. Or you could explicitly add the request to the context dictionary render_to_response('foo', {'request': request})
https://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request
Note that if you add the request context processor, you should remember to add the defaults as well.