A thread-safe template context processor in Django? - django

What is the best practice for writing a thread-safe context processor in Django?
Say, I want to pass some variables to templates, which
are set in the corresponding views, and could be different for
different view-template pairs.
One solution would be to manually pass each variable in the context:
return render_to_response('template.html', {'var1':var1,... 'var10':var10},
context_instance=RequestContext(request))
To keep it DRY, however, I would rather use a context processor. But I
worry about thread safety as it seems to require a global store.
Here is my solution using a context processor, which ties each
variable to the request. Thanks for your comments and suggestions.
In context_processor.py:
store = {}
def add_context(request, key, value):
if request not in store:
store[request] = {}
store[request][key] = value
return
def misc_context_processor(request):
return store.pop(request,{})
In views.py:
import context_processor
def view(request):
...
if x == y:
context_processor.add_context(request,'var1','value1')
else:
context_processor.add_context(request,'var2','value2')
...
return render_to_response('template.html', {},
context_instance=RequestContext(request))
In settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
...,
'appname.context_processor.misc_context_processor',
)

context_processors are for context variables you want set up in every view to be available for every template. If you have view specific context, that rightfully belongs in the view. If you're trying to push the construction of view specific context dictionaries off to a context_processor then your really creating an unnecessary headache and a landmine if anyone else ever has to touch your code. Use the tools for what they are meant to be used for.
Additionally it's a lot easier to write and read:
context = {
'var1': value1,
'var2': value2,
}
than it is to try to figure out what this is doing:
context_processor.add_context(request, 'var1', value1)
context_processor.add_context(request, 'var2', value2)
Or maybe what your want is like this:
def view(request):
context = {}
...
if x == y:
context['var1'] = value1
else:
context['var2'] = value2
...
return render_to_response('template.html', context,
context_instance=RequestContext(request))
Or maybe even use context.update({ 'var1': value1 })
I'm missing how the second one is more DRY. Considering you're going to have to do that in every view that needs those variables anyway...
If you have repeatable context generation use class based views to abstract that out in a reasonable fashion. If you really just have 10variables and each template only needs some of them (but they vary from template to template) then just simply make all of them available to all templates. As long as the generation isn't expensive this works great, and keep in mind that querysets are lazy so if you never evaluate them they never hit the db

Related

Django, how to reuse variable results get in a views in another one?

I have the following question for you. I want to know the best and elegant way to reuse some variable results obtained in a view in another one.
I'm trying to explain me better with an example.
I'have 2 differente app, app A and app B.
In app A views.py I have a lot of code to process data from the models with a creation of a lot of new variables.
Now, in app B I have the necessity to reuse some variables obtained in views.py app A, but I wonna write again the same code to get them.
Is there a way to achive my goal?
EDIT
Following the #sandeshdaundkar's suggestions I could overcome my problem creating a new function and call it in my views.py of each app.
I'm trying to achive this result but I'm not good at python's function and how call it inside my views.py.
I have created the following function in my_app/utility.py:
def my_func():
defaults = list(0 for m in range(13))
iva_debito = dict()
for year, month, totale in(Ricavi.objects.values_list( 'data_contabile__year', 'data_contabile__month').
annotate(totale=ExpressionWrapper(Sum(F('quantita') * F('ricavo')*(F('iva'))),
output_field=FloatField())).values_list('data_contabile__year', 'data_contabile__month', 'totale')):
if id not in iva_debito.keys():
iva_debito[id]=list(defaults)
index=month
iva_debito[id][index]=totale
iva_debito_totale={'IVA a Debito Totale': [sum(t) for t in zip(*iva_debito.values())],}
context= {
'iva_debito_totale':iva_debito_totale,
}
return context
And here my my_app/views.py:
...
from .utility import my_func
def iva(request):
data = my_func()
iva_debito_totale=data['iva_debito_totale']
context= {
'iva_debito_totale': iva_debito_totale,
}
return render(request, 'iva/iva.html', context)
I have solved it with the solution above.
def func_name():
# do calculation,
return context_for_view_one, obj_to_reuse
def view_one(request):
context, iva = func_name()
return render(context=context) # context to be used in view a
def view_two(request):
context, iva = func_name()
return render(context=iva) # iva to be used in view b
def iva(request):
data = func_name()
iva_debito_totale = data['iva_debito_totale']
return render(request, 'template.html', context={'totale': iva_debito_totale})
I hope this gives you an idea, we return 2 objects from common function. One which is to be used in view_one and others to be used in view_two. You can try something similiar

Django render() in 2.X

I ran into this problem while trying to capture POST data.
In Django 1.X, I structured my views like this:
def view_name(request, template_name='template.html'):
variable1 = data
variable2 = moreData
return render_to_response(template_name, locals(), context_instance=RequestContext(request))
Now, I see that render_to_response() has been deprecated, but I'm unsure how to port the code with all of the locals() calls.
Do I have to convert all of my views by building a dict with all desired variables?
Is there another way to port this to keep the locals() incorporation?
Thanks!
You can call this with:
def view_name(request, template_name='template.html'):
variable1 = data
variable2 = moreData
# …
return render(request, template_name, locals())
That being said, please do not use locals(). It makes it very unclear what exactly you pass to the template. Furthermore you often will pass more to the template than you want to. Finally most IDEs will say a variable is not used, and thus you might remove it, but it is in fact used in the template.

call variable from method inside the method

Having two views
views.py
def followup(request):
''''''
marklist = report_template(request)
return render(request, 'incident/followup.html',{'somevar':somevar})
def report_template(request):
'''''
report=Report.objects.filter(report=report_id)
''''''
return render(request, 'incident/print.html',
{'report':report})
I am calling one method inside another method.Calling report_template method into followup method.followup method also have variable to render in template.
How to pass the variable of report_template method to followup method and make it display in template.
Thanks
One way to achieve this would be to create a helper method that returns the report or the context needed by both the methods. The issue here is you are returning a HTTPResponse object, and it is cleanest to use helper method here:
def get_report(request):
report=Report.objects.filter(report=report_id)
#do more processing here.
#returning a dict is probably safest here, because, in the calling method, `.get()` would not throw an error if key is not present.
return {'report': report, 'somevar': somevar}
def followup(request):
marklist = get_report(request).get('somevar')
return render(request, 'incident/followup.html',{'somevar':marklist})
def report_template(request):
report = get_report(request).get('report')
return render(request, 'incident/print.html',
{'report':report})
You can not do that with views, you have to look up for templatetags, which is a better way to do so in Django (a view accepts a Request argument and returns an HttpResponse variable).
See https://docs.djangoproject.com/en/dev/howto/custom-template-tags/.
Using templatetags makes your code reusable, by factoring what you do in views.

Django - Access Context Dictionary Before Template

I'm hoping to use a context processor or middleware to modify the values of the dictionary passed to render_to_response prior to the actual rendering. I have a messaging schema I'm trying to implement that would populate a message list based on the existence of a type of user that I'd like to search the context for prior to the rendering of the template.
Example:
def myview(...):
...
return render_to_response('template.html',
{'variable': variable},
)
and I'd like to be able to add additional information to the context on the existence of 'variable'.
How can I access 'variable' after my view defines it but before it gets to the template so that I can further modify the context?
use TemplateResponse:
from django.template.response import TemplateResponse
def myview(...):
...
return TemplateResponse(request, 'template.html',
{'variable': variable},
)
def my_view_wrapper(...):
response = my_view(...)
variable = response.context_data['variable']
if variable == 'foo':
response.context_data['variable_is_foo'] = True
return response
This is easy. If you have supplied just a little bit more code in your example the answer might have bit you.
# first build your context, including all of the context_processors in your settings.py
context = RequestContext(request, <some dict values>)
# do something with your Context here
return render_to_response('template.html', context)
Update to comment:
The result of a render_to_response() is an HTTPResponse object containing a template rendered against a Context. That object does not (to my knowledge) have a context associated with it. I suppose you could save the result of render_to_response() in a variable and then access the Context you passed it, but I'm not sure what problem you are trying to solve.
Did you modify the Context during rendering? If so you may find that the information is not there any longer because the Context has a scope stack which is pushed/popped during template processing.
You can create a dictonary for the context:
def myview(...):
c = dict()
c["variable"] = value
...
do some stuff
...
return render_to_response('template.html',c)
Maybe the RequestContext is the thing you are looking for.

How to Pass Django form values to a model class method

What is the best way to pass request information to a model class method?
I'm wondering whether I have my logic in the wrong place. Maybe I need to move it out of my model.
I want to be able to pass in POST variables or a form to filter the model by country or institution.
I can't do that from the template, but the question is whether I should do that from within the model or controller somehow.
My Model:
class AccountExtra(User):
def myPendingPaymentSchedules(self, status=1, **args):
if self.is_staff: # status = approved by MFI
schedules = PaymentSchedule.objects.select_related(depth=3).filter(active=True)
if country:
schedules = schedules.filter(country=country)
if institution:
schedules = schedules.filter(institution=institution)
return schedules
My Controller:
myAccount = get_object_or_404(AccountExtra, id=request.user.id)
My Template
{% for sample in myAccount.myPendingPaymentSchedules %} # Can't pass parameters for country, etc
Yes, I'd say your logic is in the wrong place. I don't know where the values are coming from that you're trying to pass into myPendingPaymentSchedules, but it seems like it should be done in the view rather than the template. Then you can pass the resulting schedules directly into the template context.
(By the way, your naming scheme is not very Pythonic: I'd use my_account and my_pending_payment_schedules - see PEP8.
Thanks for the feedback. I've done a little bit of research about how to access business login from within templates and I thought I'd provide an update in case others find this question in the search results:
There are two cases in which the parameters for the method need to be passed:
Case #1) Passing paramaters for a single value
If we only have a single account, we can simply pass them to the model through a single call in the controller, and pass the result to the template as a single context variable.
Model
class AccountExtra(models.Model):
..
def my_pending_payment_schedules(self, status=1, country=None, institution=None)
if self.is_staff:
schedules = payment_schedule.objects.filter(active=True)
if country:
schedules = schedules.filter(product__country=country)
if institution:
schedules = schedules.filter(product__institution=institution)
return schedules
Controller
my_account = get_object_or_404(AccountExtra, id=request.user.id)
form = staff_approval_form(request.POST)
if form.is_valid():
cd = form.cleaned_data
pending_schedules = my_account.my_pending_payment_schedules(
country=cd.get('country', None),
institution=cd.get('institution', None)
)
c = RequestContext( request, {
'form': form,
'pending_schedules': pending_schedules,
})
return render_to_response(
'my/approvals/approvals_index.html',
context_instance=RequestContext(request, c)
)
Template
{% for sample in pending_schedules %}
Case #2: Passing parameters for multiple values
If however, we are trying to iterate through multiple users's pending schedules who each require different parameters, we can't use a simple 'pending_schedules' variable.
We have to either turn that variable into a dictionary to store the results of multiple users.
A collegue of mine developed a template tag that allows you to access the dictionary by key, as you iterate through the loop.
Templatetags
#register.filter
def hash(obj, key):
"""return hash lookup of key in object
If the key can be hard-coded into the template, then the normal dot-notation
is sufficient (obj.key). But if the key is referenced by a name in the
template context then this hash filter becomes useful.
Template usage: obj|hash:key
"""
return obj[key]
Template:
for user in users:
for sample in pending_schedules|hash:user.id
Do something
endfor
endfor