Django view not updating context properly - django

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.

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)

Django: How to integrate an app inside another app

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.

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.

How can I pass variables to a view using a context processor?

I am using a context processor to pass several variables to all my templates. However, I would also like use these variables in the actual views that render the respective templates. Should I add them to the session object of the request object or to the request object itself (if at all possible)?
Use RequestContext:
def my_view(request):
c = RequestContext(request)
# c['key'] gets the value for 'key' from your context processor.
return render_to_response('template.html', {}, context_instance = c)
Can't you just get a reference to the context processor and call it in your views? From what I read in the docs, there's nothing special about a context processor:
A context processor has a very simple interface: It's just a Python function that takes one argument, an HttpRequest object, and returns a dictionary that gets added to the template context. Each context processor must return a dictionary.
Custom context processors can live anywhere in your code base. All Django cares about is that your custom context processors are pointed-to by your TEMPLATE_CONTEXT_PROCESSORS setting.
You could have each view access them in the beginning, passing it the request parameter, or maybe create a decorator that would "inject" it in your views for you (whatever's easier in your case).

Passing template variables from URL to FormPreview in Django

I'm a Django noob and fear the answer to my question is fairly obvious, but hoping someone can help.
I'm building an app that includes the same form on every page, with the content surrounding the form and the model instance to which the form data is tied dependent on a value passed in the URL. Works fine using the standard Form class and (URL, 'template.html', myapp.view) in URLconf, like so:
url(r'^listings/(?P<listing_id>\d+)/submit$', 'myapp.views.lister'),
With FormPreview, however, instead of calling the view in the URLconf, you're calling the subclass with the view functionality baked in.
url(r'^listings/(?P<listing_id>\d+)/submit$', PickFormPreview(PickForm)),
From what I can gather from the docs, FormPreview uses parse_params to pass values captured in the URL to state.self, which I believe is a dictionary. Unfortunately given my level of experience, I can't figure out from this barebones understanding how to customize my FormPreview subclass how to pass the listing_id captured in the URL to a template variable in my form.html template called by FormPreview. Do I somehow need to override parse_params? Or somehow pass state.listing_id? Or am I missing it entirely?
Any help much appreciated!
You're on the right track. The parse_params method in FormPreview does not do anything, but it is the correct method to override in your subclass.
The following example saves listing_id to self.state.listing_id, which is available in the template context.
class PickFormPreview(FormPreview):
def parse_params(self, *args, **kwargs)
"""
Saves listing_id from the url to state
"""
self.state['listing_id'] = kwargs['listing_id']
Then in your template, you access it as:
Listing {{ state.listing_id }}