Setting a variable in middleware to be accessed in the template - django

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.

Related

How to get url params from the HttpRequest object

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)

How to safely access request object in Django models

What I am trying to do:
I am trying to access request object in my django models so that I can get the currently logged in user with request.user.
What I have tried:
I found a hack on this site. But someone in the comments pointed out not to do it when in production.
I also tried to override model's __init__ method just like mentioned in this post. But I got an AttributeError: 'RelatedManager' object has no attribute 'request'
Models.py:
class TestManager(models.Manager):
def user_test(self):
return self.filter(user=self.request.user, viewed=False)
class Test(models.Model):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(Test, self).__init__(*args, **kwargs)
user = models.ForeignKey(User, related_name='test')
viewed = models.BooleanField(default=False)
objects = TestManager()
I trying to access request object in my Django models so that I can get the currently logged in user with request.user.
Well a problem is that models are not per se used in the context of a request. One for example frequently defines custom commands to do bookkeeping, or one can define an API where for example the user is not present. The idea of the Django approach is that models should not be request-aware. Models define the "business logic" layer: the models define entities and how they interact. By not respecting these layers, one makes the application vulnerable for a lot of problems.
The blog you refer to aims to create what they call a global state (which is a severe anti-patten): you save the request in the middleware when the view makes a call, such that you can then fetch that object in the model layer. There are some problems with this approach: first of all, like already said, not all use cases are views, and thus not all use cases pass through the middleware. It is thus possible that the attribute does not exist when fetching it.
Furthermore it is not guaranteed that the request object is indeed the request object of the view. It is for example possible that we use the model layer with a command that thus does not pass through the middleware, in which case we should use the previous view request (so potentially with a different user). If the server processes multiple requests concurrently, it is also possible that a view will see a request that arrived a few nanoseconds later, and thus again take the wrong user. It is also possible that the authentication middleware is conditional, and thus that not all requests have a user attribute. In short there are more than enough scenario's where this can fail, and the results can be severe: people seeing, editing, or deleting data that they do not "own" (have no permission to view, edit, or delete).
You thus will need to pass the request, or user object to the user_test method. For example with:
from django.http import HttpRequest
class TestManager(models.Manager):
def user_test(self, request_or_user):
if isinstance(request_or_user, HttpRequest):
return self.filter(user=request_or_user.user, viewed=False)
else:
return self.filter(user=request_or_user, viewed=False)
one thus has to pass the request object from the view to the function. Even this is not really pure. A real pure approach would only accept a user object:
class TestManager(models.Manager):
def user_test(self, user):
return self.filter(user=user, viewed=False)
So in a view one can use this as:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
# return Http response
For example if we want to render a template with this queryset, we can pass it like:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
return render(request, 'my_template.html', {'some_tests': some_tests})

django middleware difference between process_request and process_view

I am little bit confused on process_request and process_view.
Process request is something that u want to pass on view with request. Example can be taken from request.user.
Then what does process_view does ? Is it for executing any view initially befor calling any url ? Like initially I want to show home view but this can be done from url too.
Can anyone give me example when to use process_view ?
Thank you
process_request is called before Django determines which view should handle the request (hence, its only parameter is the request).
process_view is called after Django determines which view will handle the request, but before that view is called. It will have access to the request object, along with the view that will handle it and the parameters that will be passed to that view.
Whenever you need to know the view that will be used for a request, you can use process_view. A good example for this is Django's CSRF Middleware process_view, which will not enforce CSRF protection if a csrf_exempt decorator is present on the view the request is meant for:
def process_view(self, request, callback, callback_args, callback_kwargs):
[...]
if getattr(callback, 'csrf_exempt', False):
return None
[...]
Adrian Ghiuta have written a very good answer. I just want to add few points to that.
process_request is called before doing the url matching and process_view is called after url matching but before invoking that view.
We can use the process_request to change the url itself and thus invoke a different view. This point helped me understand these, so I thought to answer it, maybe it'll someone else too.

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()

Django view not updating context properly

So I have a custom template tag, which is rather simple. It takes a view name, renders it, and returns its content. The problem is, while using .as_view() on them gets me the content, it doesn't seem to properly set the context, because django-sekizai's addtoblocks do not properly add to main template.
On the other hand, if I instance the view directly and call .render_to_response(context).render() on it, the context will update and sekizai will inject block data into base template. However, this second approach will not correctly render forms (which .as_view() does). Is there a way to do this without dirtily calling both?
class PopupNode(template.base.Node):
def __init__(self, popup):
self.popup = popup
def render(self, context):
# only needed to update context
view_object = self.popup()
view_object.request = context['request']
view_object.render_to_response(context).render()
# actual content
view_function = self.popup.as_view()
template_response = view_function(context['request'], context=context)
template_response.render()
return template_response.content
As it turns out, the inherited get() function disregards any pre-existing context and renders its own (which is logical, since views do not expect to be rendered within other views), thus not letting sekizai update it. calling .render_to_template() directly allows you to supply the context, so it works. Overriding the get() method to accept prior context and inject its own worked.