I'm trying to write a django middleware using process_template_response but I seems like it's not working, can anybody help me with this or maybe give an example on how to use the method.
below is my code:
class MiddleWare(object):
def process_template_reponse(self, request, response):
response.context_data = dict(title='title')
response.template_name = 'pages/helloworld.html'
return response
in settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'proj.app.middleware.MiddleWare', # here my middleware
)
in template
<!-- nothing is showing -->
{% block title %}{{ title }}{% endblock %}
by the way I'm using Django 1.3 rc 1
Thanks
http://docs.djangoproject.com/en/dev/topics/http/middleware/#process-template-response
Are you using the new SimpleTemplateResponse response classes?
Your view must return SimpleTemplateResponse or subclass for the new process_template_response middleware hook to work.
Copied from the docs:
def my_view(request):
# Create a response
response = TemplateResponse(request, 'mytemplate.html', {})
# Register the callback
response.add_post_render_callback(my_render_callback)
# Return the response
return response
TL;DR: You must render your templates with TemplateResponse:
from django.template.response import TemplateResponse
def myView(request):
context = {'foo':'foo_value'}
return TemplateResponse(request,'foo.html', context)
The problem arises when you render your templates with the good old render imported from django.shorcuts. As stated in the documentation for process_template_response(), this hook gets called when the response is a TemplateResponse or equivalent. So what you should do is to use TemplateResponse to render your templates and your middleware will work.
You can also alias TemplateResponse as render so you don't have to change all your views. I personally don't recommend this.
from django.template.response import TemplateResponse as render
Here is a working example of a simple middleware that uses the process_template_response method.
class ExampleMiddleware(object):
def process_template_response(self, request, response):
response.context_data['title'] = 'We changed the title'
return response
This middleware changes the value of the title variable in the template context data. Install this middleware by adding it to MIDDLEWARE_CLASSES in your settings file. Then visit any page in the django admin app you should see the title of the page change to We changed the title.
I've solved my problem creating a custom template tag. I'm just wondering on how to add a context variable using a process_template_reponse in a middleware.
Related
I want to provide some context to all my function-based views (FBV) similar to the way TEMPLATE_CONTEXT_PROCESSORS (CP) provides context to all of one's templates. The latter doesn't work for me because I need that context prior to rendering the templates.
In particular, on my site I have a function which takes a request and returns the model for the Category of item in focus. My CP provides this for all templates, but I find myself making the same call from my FBV's and would like to remove this redundancy.
This question is similar but it presupposes the approach of accessing the output of the CP from the views. This seems hacky, and I'm not sure it's the best approach.
What's the Django way to do this?
Use Middleware...
class MyModelMiddleware(object):
def process_request(self, request):
request.extra_model = self.get_model(request.user)
Based on mwjackson 's answer and on docs, for Django 1.11, I think the middleware should be:
# middleware/my_middleware.py
class MyModelMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
# TODO - your processing here
request.extra_model = result_from_processing
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
In settings.py, add the path to your Middleware on MIDDLEWARE = () . Following the tips from this site, I had created a folder inside my app called middleware and added a new file, say my_middleware.py, with a class called, say, MyModelMiddleware. So, the path that I had added to MIDDLEWARE was my_app.middleware.my_middleware.MyModelMiddleware.
# settings.py
MIDDLEWARE = (
...
'my_app.middleware.my_middleware.MyModelMiddleware',
)
I'm trying to work with the following scenario:
When a GET request comes in to my "/" route I normally want to handle it with my HomeView. However, my site is heavy AJAX so if the request's UserAgent is a bot then I serve it up with a fully rendered version of the page (standard PhantomJS stuff). The approach works fine, but the performance of the fully rendered version, and the SLA for that version, is very different than the regular user view. As such, I would like to use a piece of middleware to do the bot detection and based on that middleware I would then like to send the request to a different View.
The middleware part is easy, I have a process_request handler that detects the bot - no big deal. However, I can't figure any option for overriding the View function that will be invoked. Is there a "proper" way to do this in Django? My current thoughts are:
modify request.path_info to change the requested URL so that the router will then send HtmlRendererView rather than HomeView
Call the HtmlRendererView directly from the middleware and return the appropriate HttpResponse. This feels clunky because it then takes away the opportunity for any other middleware to run.
Notes:
I don't want to return a redirect, the crawler is getting a different version of the same resource
I'm on heroku so I can't rewrite the route before it hits Django. If I was using nginx I'd probably just put this logic at that layer and rewrite the URL before it hit Django.
This is not a direct answer to your question ("Reroute a request to a different view"), but maybe this solution could adress your problem.
First, you keep your middleware, but use it only to detect if the visitor is a bot:
def process_request(self, request):
request.is_bot = is_bot(request) # assuming you have a function for detecting bots
return
Then you create a class based view that call a specific method when request.is_bot is True:
class BotViewMixin(object):
def dispatch(self, request, **kwargs):
if request.is_bot:
return self.handle_bot()
return super(BotViewMixin, self).dispatch(request, **kwargs)
You can then inherit this view anywhere you need (e.g. for your Home Page View). You just have to create a handle_bot method on your view, that will return your response for bots.
Advantages of this solution:
You don't need to write different views for bots, just create a dedicated method
You don't block other middlewares
Your logic stay in your views (and not in your middleware)
This is not tested though, so you may need to adapt the code.
EDIT:
Since you use NewRelic and must use a dedicated view for bots in order to get accurate statistics, this approach won't work for you.
You can go with the middleware thing, and still get all middlewares working. You just have to put your own middleware last in MIDDLWARE_CLASSES:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.CrawlerDetector',
)
Also, I think you should write two middlewares methods: process_request for detecting bots, and process_view for redirecting bots to dedicated view.
The following code should probably work for your situation:
from django.core.urlresolvers import reverse
class CrawlerDetector(object):
def process_request(self, request):
"""detect if the user agent is a bot"""
user_agent = request.META.get('HTTP_USER_AGENT', "")
request.is_bot = self.is_crawler(user_agent)
return
def process_view(request, view_func, view_args, view_kwargs):
if request.is_bot and request.path == reverse('home_page'):
return HtmlRendererView().get(request)
return
My current working solution, while not as clean as Eliot's suggested solution, looks (basically) like this:
class CrawlerDetector(object):
# Middleware that detects requests that should be rendered by the HtmlRendererView.
def process_request(self, request):
user_agent = request.META.get('HTTP_USER_AGENT', "")
if not self.is_crawler(user_agent):
return None
return HtmlRendererView().get(request)
It has the downside of removing any downstream middleware from the flow, but it does allow me to call my crawler-specific View before the root View is routed to.
I've been spending hours trying to find a way to have Basic HTTP Authentication for one of my views. These are several of the solutions I've tried but have had no success. The request is still be processed even with no authentication. I'm using version 1.4.3 of Django. Here is my Django view:
#csrf_exempt
def facebook(request):
if request.user.is_authenticated():
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
else:
return HttpResponse("This user is not authenticated")
I sent in the request without authentication, and it still returned results. This is not suppose to happen.
Another solution I tried was from a Django Snippet I found called, view by view basic authentication decorator
I made a httpauth.py and copied the code over from the snippet:
from mydjangoapp.httpauth import *
#csrf_exempt
#logged_in_or_basicauth()
def facebook(request):
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
I sent the request without authentication, and it still returned results. After exhausting all options, I turned to Django's very own #login_required decorator:
from django.contrib.auth.decorators import login_required
#csrf_exempt
#login_required
def facebook(request):
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
Here is more information about my settings.py:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
After trying all of these options, I don't know what to do. Is there anything I am missing here?!
This really isn't much of an answer. I'm sorry that I have to post it here, but the system has cut me off from the comment section.
I don't see any issues with your example with using the #login_required decorator. This is typically how I do it on my Django sites. This leads me to believe that you have 1 of 2 things going on here:
You have a configuration issue in your settings file
During initial testing, you have actually authenticated and created a session.
Again, I don't think your problem is with your code. Please post what you finally determine is the issue so that I (and others) may learn from it.
I have site with many views and I want to check the cookie in each of them, and when it does not - save them. But site have a lot of views.
How to do it only once for all views?
You can write custom middleware to achieve your goal as you have many views and of course you can not update every view. The custom middleware would be something like this:
class MyCookieProcessingMiddleware(object):
# your desired cookie will be available in every django view
def process_request(self, request):
# will only add cookie if request does not have it already
if not request.COOKIES.get('your_desired_cookie'):
request.COOKIES['set_your_desired_cookie'] = 'value_for_desired_cookie'
# your desired cookie will be available in every HttpResponse parser like browser but not in django view
def process_response(self, request, response):
if not request.COOKIES.get('your_desired_cookie'):
response.set_cookie('set_your_desired_cookie', 'value_for_desired_cookie')
return response
In your settings.py file, just add the path to your custom middleware like this:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'MyProject.myapp.mymodule.MyCookieProcessingMiddleware', # path to custom class
)
The order of middleware is important and yours belongs after SessionMiddleware.
What I understood is that, you want to set the cookie once and then want to check it's value in any view. If this is your problem then you can save cookie once in views like this:
from project.settings import IS_COOKIE_SET # Set Global value for cookie
response = render_to_response("your-template.html")
if !IS_COOKIE_SET:
response.set_cookie('key', 'value')
return response
else:
return response
You can check the value of cookie in any other view like this:
request.COOKIES.get('key', None) # Return None If cookie not exists
My question is simple, how do I display a custom error page for HTTP status 405 (method not allowed) in Django when using the #require_POST decorator?
I'm using the django.views.decorators.http.require_POST decorator, and when the page is visited by GET request, the console shows a 405 error, but the page is just blank (not even a Django error page). How do I get Django to display a custom and/or default error page for this kind of error?
EDIT:
It's worth mentioning that I've tried putting a 404.html, 500.html and 405.html page in my templates folder - but that does not help either. I have also varied between DEBUG = True and False, to no avail.
You have to write custom Django middleware. You can start with this one and extend it to check if 405.html file exists and so on:
from django.http import HttpResponseNotAllowed
from django.template import RequestContext
from django.template import loader
class HttpResponseNotAllowedMiddleware(object):
def process_response(self, request, response):
if isinstance(response, HttpResponseNotAllowed):
context = RequestContext(request)
response.content = loader.render_to_string("405.html", context_instance=context)
return response
Check docs if you don't know how to install middleware:
http://docs.djangoproject.com/en/dev/topics/http/middleware/
You can also check this article:
http://mitchfournier.com/2010/07/12/show-a-custom-403-forbidden-error-page-in-django/
If you look into the documentation and the source code of django.views.defaults you see that only 404 and 500 errors are supported in a way that you only have to add the 404.html resp. 500.html to your templates directory.
In the doc. you can also read the following
Returning HTTP error codes in Django
is easy. There are subclasses of
HttpResponse for a number of common
HTTP status codes other than 200
(which means "OK"). You can find the
full list of available subclasses in
the request/response documentation.
Thus if you want to return a 405 error, you have to use the HttpResponseNotAllowed class
An example
I'm not sure that's possible. Perhaps you should consider filing a bug report.
I landed here in 2022. The above accepted answer is not working for me. I use django rest framework. My sollution was to create a middleware with
#app/middleware.py
from django.http import HttpResponse
from django.template import loader
class HttpResponseNotAllowedMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
if response.status_code == 405:
context = {}
template = loader.get_template('app/405.html')
return HttpResponse(template.render(context, request))
return response
then install it by adding this to settings
MIDDLEWARE = [
.......
'app.middleware.HttpResponseNotAllowedMiddleware',
]
the 405.html template is just a plain not allowed text