Django custom context_processors in render_to_string method - python

I'm building a function to send email and I need to use a context_processor variable inside the HTML template of the email, but this don't work.
Example:
def send_email(plain_body_template_name, html_body_template_name):
plain_body = loader.render_to_string(plain_body_template_name, context)
html_body = loader.render_to_string(html_body_template_name, context)
email_msg = EmailMultiAlternatives(body=plain_body)
email_msg.attach_alternative(html_body, 'text/html')
email_message.send()
In my custom context_processor.py I just have a function that receive a HttpRequest and return a dict like {'foo': 'bar'}, and in the template I try to render using {{foo}}.
I added the context_processor in the TEMPLATE['OPTIONS']['context_processors'] too.

Assuming you're using the django backend in your TEMPLATE with
'BACKEND': 'django.template.backends.django.DjangoTemplates',
django is seeing that you haven't passed in a request and opting for a basic Context to wrap your dict instead of a RequestContext which will handle the context_processors you've defined.
You can probably get away with doing
html_body = loader.render_to_string(html_body_template_name, context, request=request)
but you'd need to pass in the request object.
This might not make sense though. Are you emailing the person making the request? Does the context make sense to include?
If your context processor doesn't need the request then I'd either make it a simple utility function (if it's only called here) or make the request parameter optional, import it into this module, and add it directly into the context
context = {"my_var": 1}
context.update(your_extra_context())
loader.render_to_string(...)
There are some complicated ways of updating a Context() in layers, but I don't think that's necessary here.

I had a similar problem - I needed to render template to string with context processor values, but at the same time request object was None (running command from console). Then I found this approach:
from django.template.loader import render_to_string
from django.template import RequestContext
from django.shortcuts import render
def index(request):
if not request:
context = {'param1':'value1'}
return render_to_string('myapp/index.html', RequestContext(None, context))
else:
#render as usual
render(request, 'myapp/index.html', context)
pass
when you pass RequestContext instead of dictionary, it populates values of all context processors into the context. But request must be optional in all your context processors, otherwise this won't work.

Related

Django GET and POST handling methods

I want a way to automatically route GET and POST requests to subsequent methods in a centralized way.
I want to create my handler in the following way.
class MyHandler(BaseHandler):
def get(self):
#handle get requests
def post(self):
#handle post requests
This is what webapp2 does and I very much like the style, is it possible to do in Django?
I also want the view in Class-method style. What kind of BaseHandler and router should I write.
HINT: Use django generic views.
This is supported in Django as class based views. You can extend the generic class View and add methods like get(), post(), put() etc. E.g. -
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('This is GET request')
def post(self, request, *args, **kwargs):
return HttpResponse('This is POST request')
The dispatch() method from View class handles this-
dispatch(request, *args, **kwargs)
The view part of the view – the
method that accepts a request argument plus arguments, and returns a
HTTP response.
The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a GET will be
delegated to get(), a POST to post(), and so on.
By default, a HEAD request will be delegated to get(). If you need to
handle HEAD requests in a different way than GET, you can override the
head() method. See Supporting other HTTP methods for an example.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
Then you can use it in urls.py -
from django.conf.urls import patterns, url
from myapp.views import MyView
urlpatterns = patterns('',
url(r'^mine/$', MyView.as_view(), name='my-view'),
)

Django: href {% url %} issue

Why does
View answers
in my template translate to this interpretation by Django:
Request URL: http://127.0.0.1:8000/questions/%7B%%20url%20'answers.views.display_answers'%20Question.id
which of course leads to an url mismatch error.
Seems like its reading in my '{' in ASCII form. Can anyone enlighten me as to why it is so?
EDIT:
This was how i rendered the template--
return render(request, 'display_questions.html', context)
and the template contains the href. My display answer view redirects to another view as such:
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return redirect('view_answers.html', context)
Error:
The current URL, questions/{% url 'answers.views.display_answers' Question.id, didn't match any of these.
This is correct. If not - your urls.py seems to be wrong. Please post it.
View answers
Edit
Here's a better version of your view.
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, redirect, get_object_or_404
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return render_to_response('view_answers.html', context, RequestContext(request))
The problem is your use of redirect in the view. You should be using render or render_to_response unless you actually want to redirect the browser. (Observe using Fiddler, Firebug, or Chrome's developer tools and you'll see that it is redirecting.)
The reason this is not as obvious of a problem is because redirect may take a URL as its first argument. 'view_answers.html' is being interpreted as a relative URL, which may or may not map to a URL in your URLconf. If it does map to a URL, then you get a false positive result that everything appears to be working, but if your web server handles that link instead of Django, then it may just send the template page back in plain text. The solution is, as I said, either render or render_to_response for rendering a page, or redirect with the name of a view or the special name of a URL pattern to redirect to a different view.
Modify your redirect in your display_answers view to use the name of the view, instead of the name of your template (view_answers.html) and don't pass it the context (redirect doesn't take the context as a parameter):
return redirect('your_view_answers_view')

How do I return JSON without using a template in Django?

This is related to this question: Django return json and html depending on client python
I have a command line Python API for a Django app. When I access the app through the API it should return JSON and with a browser it should return HTML. I can use different URLs to access the different versions but how do I render the HTML template and JSON in the views.py with just one template?
To render the HTML I would use:
return render_to_response('sample/sample.html....')
But how would I do the same for JSON without putting a JSON template? (the content-type should be application/json instead of text/html)
What would determine the JSON and HTML outputs?
So in my views.py:
if something:
return render_to_response('html_template',.....)
else:
return HttpReponse(jsondata,mimetype='application/json')
I think the issue has gotten confused regarding what you want. I imagine you're not actually trying to put the HTML in the JSON response, but rather want to alternatively return either HTML or JSON.
First, you need to understand the core difference between the two. HTML is a presentational format. It deals more with how to display data than the data itself. JSON is the opposite. It's pure data -- basically a JavaScript representation of some Python (in this case) dataset you have. It serves as merely an interchange layer, allowing you to move data from one area of your app (the view) to another area of your app (your JavaScript) which normally don't have access to each other.
With that in mind, you don't "render" JSON, and there's no templates involved. You merely convert whatever data is in play (most likely pretty much what you're passing as the context to your template) to JSON. Which can be done via either Django's JSON library (simplejson), if it's freeform data, or its serialization framework, if it's a queryset.
simplejson
from django.utils import simplejson
some_data_to_dump = {
'some_var_1': 'foo',
'some_var_2': 'bar',
}
data = simplejson.dumps(some_data_to_dump)
Serialization
from django.core import serializers
foos = Foo.objects.all()
data = serializers.serialize('json', foos)
Either way, you then pass that data into the response:
return HttpResponse(data, content_type='application/json')
[Edit] In Django 1.6 and earlier, the code to return response was
return HttpResponse(data, mimetype='application/json')
[EDIT]: simplejson was remove from django, you can use:
import json
json.dumps({"foo": "bar"})
Or you can use the django.core.serializers as described above.
In Django 1.7 this is even easier with the built-in JsonResponse.
https://docs.djangoproject.com/en/dev/ref/request-response/#jsonresponse-objects
# import it
from django.http import JsonResponse
def my_view(request):
# do something with the your data
data = {}
# just return a JsonResponse
return JsonResponse(data)
In the case of the JSON response there is no template to be rendered. Templates are for generating HTML responses. The JSON is the HTTP response.
However, you can have HTML that is rendered from a template withing your JSON response.
html = render_to_string("some.html", some_dictionary)
serialized_data = simplejson.dumps({"html": html})
return HttpResponse(serialized_data, mimetype="application/json")
For rendering my models in JSON in django 1.9 I had to do the following in my views.py:
from django.core import serializers
from django.http import HttpResponse
from .models import Mymodel
def index(request):
objs = Mymodel.objects.all()
jsondata = serializers.serialize('json', objs)
return HttpResponse(jsondata, content_type='application/json')
It looks like the Django REST framework uses the HTTP accept header in a Request in order to automatically determine which renderer to use:
http://www.django-rest-framework.org/api-guide/renderers/
Using the HTTP accept header may provide an alternative source for your "if something".
You could also check the request accept content type as specified in the rfc. That way you can render by default HTML and where your client accept application/jason you can return json in your response without a template being required
from django.utils import simplejson
from django.core import serializers
def pagina_json(request):
misdatos = misdatos.objects.all()
data = serializers.serialize('json', misdatos)
return HttpResponse(data, mimetype='application/json')
Here's an example I needed for conditionally rendering json or html depending on the Request's Accept header
# myapp/views.py
from django.core import serializers
from django.http import HttpResponse
from django.shortcuts import render
from .models import Event
def event_index(request):
event_list = Event.objects.all()
if request.META['HTTP_ACCEPT'] == 'application/json':
response = serializers.serialize('json', event_list)
return HttpResponse(response, content_type='application/json')
else:
context = {'event_list': event_list}
return render(request, 'polls/event_list.html', context)
you can test this with curl or httpie
$ http localhost:8000/event/
$ http localhost:8000/event/ Accept:application/json
note I opted not to use JsonReponse as that would reserialize the model unnecessarily.
If you want to pass the result as a rendered template you have to load and render a template, pass the result of rendering it to the json.This could look like that:
from django.template import loader, RequestContext
#render the template
t=loader.get_template('sample/sample.html')
context=RequestContext()
html=t.render(context)
#create the json
result={'html_result':html)
json = simplejson.dumps(result)
return HttpResponse(json)
That way you can pass a rendered template as json to your client. This can be useful if you want to completely replace ie. a containing lots of different elements.

Creating a fake request to render a view to a string in django

Problem
I'd like to render an arbitrary view, by calling the view (capture the response and extract the rendered content), to a string, inside another view.
The problem being I'd like to have a dummy user "logged-in" during the rendering of that view, along with changing a few other minor things in the request.
What I'd like to avoid is building a request completely from scratch, as 90% of the request I'll have at hand in the parent view will be the same.
I'm wondering how I should go about this as far as best practice is concerned, as well as technically?
I'm currently thinking something like this: (But I can't help but feel this is horrible and there's got to be a better way, I just cannot think of it)
View stuff...
Log current user out
Create/Login dummy user
Somehow modify request a bit
Render view to string
Log out dummy user
Log back in original user
End of view stuff...
Any ideas? Or a pointer into a better direction?
Thanks,
dennmat
Depending upon how much you're relying on getting information from the request during the view, it might be best that you just create a function to do all of the view's dirty work, and have your real view just pass in the values it needs into this function. So, instead of a view function that takes a request, create another function that takes a user:
def _real_view(user):
return render_to_response('template.html', {'user' : user})
def view(request):
# Now both of these are possible...
response = _real_view(User.objects.get(5))
response = _real_view(request.user)
return response
Then, when you want to a view using a different user, you only need to grab the information for that user and pass it into the view. No need to change the current user or modify the request at all.
You don't actually need to log the current user out, you could just change the user in the HttpRequest object you plan to use to render the other view. You could do something like this:
from django.contrib.auth.models import User
from django.http import HttpResponse
def view_inside_a_view(request):
return HttpResponse('hello %s' % request.user)
def view(request):
# change to dummy user, or whoever
request.user = User.objects.get(id=1)
response = view_inside_a_view(request)
return HttpResponse('rendered view: %s' % response.content)
If you need to login your dummy user you could use django.contrib.auth.authenticate or or django.contrib.auth.login to do so. Example using login (avoids necessity of using dummy user's password):
from django.contrib.auth.models import User
from django.contrib.auth import login, get_backends
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
#login_required
def view_inside_a_view(request):
return HttpResponse('hello %s' % request.user)
def view(request):
# login dummy user
user = User.objects.get(id=2)
backend = get_backends()[0]
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
login(request, user)
# change request's user
request.user = user
# get response from view
response = view_inside_a_view(request)
return HttpResponse('rendered view: %s' % response.content)
just use the test client - it's not only good for testing:
from django.test import Client
c = Client()
# login if necessary
c.login(username='dummy', password='user')
# get the view
response = c.get(reverse('my-view', args=[foo,bar])
html = response.content
Since 1.6 this is even officially documented, however it works in older versions too: https://docs.djangoproject.com/en/1.6/topics/testing/tools/#module-django.test.client

How can I redirect after POST in Pyramid?

I'm trying to have my form submit to a route which will validate the data then redirect back to the original route.
For example:
User loads the page website.com/post
Form POSTs the data to website.com/post-save
User gets redirected back to website.com/post
Pyramid is giving me some troubles doing this.
Here's my slimmed down views.py
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
""" form data is submitted here """"
form = _get_link_form(request.POST)
if not form.validate():
return home_page(request, form)
This is the code I've been playing around with. Not only does it not work, it also feels messy and hacked up. Surely there's a simpler way to 'redirect after-POST' in Pyramid?
Your problem is most easily solved by simply POSTing to the same URL that your form is shown at, and simply redirecting the user away from the page when the POST is successful. That way until the form is successfully submitted you do not change URLs.
If you're just dying to POST to a different URL, then you need to save the data using sessions, since you're obviously handling the form data between requests.
Typically if you want to be able to handle errors in your forms you would use a session and flash messages. To do this you simply add a location for flash messages to appear in your base template and setup session support using something like pyramid_beaker.
Assuming your home page is setup at the 'home' named-route:
from pyramid.httpexceptions import HTTPFound
def myview(request):
user = '<default user field value>'
if 'submit' in request.POST:
user = request.POST.get('user')
# validate your form data
if <form validates successfully>:
request.session.flash('Form was submitted successfully.')
url = request.route_url('home')
return HTTPFound(location=url)
return {
# globals for rendering your form
'user': user,
}
Notice how if the form fails to validate you use the same code you did to render the form originally, and only if it is successful do you redirect. This format can also handle populating the form with the values used in the submission, and default values.
You can loop through the flash messages in your template of choice using request.session.peek_flash() and request.session.pop_flash().
route_url supports mutating the query string on the generated url as well, if you want to flag your home page view to check the session data.
You can obviously just pass everything in the query string back to the home page, but that's a pretty big security vulnerability that sessions can help protect against.
The Pyramid documentation has a particularly on-point section with the following example:
from pyramid.httpexceptions import HTTPFound
def myview(request):
return HTTPFound(location='http://example.com')
I do this like so:
from pyramid.httpexceptions import HTTPCreated
response = HTTPCreated()
response.location = self.request.resource_url( newResource )
return response
This sends the HTTP Created code , 201
The Pyramid documentation has content about Redirect, you can see more information in below link :
Pyramid documentation
import pyramid.httpexceptions as exc
raise exc.HTTPFound(request.route_url("section1")) # Redirect
Edited:
Actually you can do that on client side with Javascript, first you should send particular response to client side(either with flashes some data or return Response object):
window.location = '{{ request.route_path("route_name") }}';
Assuming your homepage is the default view of your pyramid web app, you can do:
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
form = _get_link_form(request.POST)
if not form.validate():
from pyramid.httpexceptions import HTTPFound
return HTTPFound(location=request.application_url)
Basically you need to know how the home_page view was "added" to your Configurator. If your homepage actually lives at /few/levels/deep/homepage then a redirect might look like this:
return HTTPFound(location=request.application_url + '/few/levels/deep/homepage')
A clean way is using the "overload" provided by pyramid for different request types, por example, you can decorate your methods this way:
#action(request_method='GET',
renderer='mypackage:/templates/save.mako',
name='save')
def save(request):
''' Fill the template with default values or leave it blank'''
return {}
#action(request_method='POST',
renderer='mypackage:/templates/save.mako',
name='save')
def save_post(request):
""" form data is submitted here """"
# process form
In the HTML, you must call the action form, like
<form method="POST" id="tform" action="${request.route_url('home', action='save')}">
This way, one method is processed when the method POST is used, and the other when the GET is used. The same name, but two implementations.

Categories

Resources