How to get url params from the HttpRequest object - django

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)

Related

Pass a query parameter with django reverse?

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.

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)

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.

call internal view function in django

I want to call a function from another function in django. The function looks like this
def main_view(request):
if request.method == 'GET':
I dont want to have the url to main_view open so that user can access this url directly, instead they should face a login page(just a lot of checkboxes that the four right ones should be choosen) and after that submit, they should come to the template that the main_view function is rendering.
But, how do I actually call function internal that need a request GET input?
Is there a way to do a GET request when calling the function?
//Mikael
You can just pass the request from your other view into that one, like this:
def my_public_view(request):
if user_passed_checkbox_test():
return main_view(request)

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.