process_template_response doesn't get called in Django - python

In views.py I use render() .
In app -> middleware.py I have this code:
from django.conf import settings
class NoTrackingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_template_response(self, request, response):
no_tracking = True if request.GET.get("NO_TRACKING", default=False) is not False else False
pub_id = "PUBLISHER_TEST" if no_tracking is True else settings.WFF_PUB_ID
response.context_data["no_tracking"] = no_tracking
response.context_data["pub_id"] = pub_id
return response
In settings.py I have:
MIDDLEWARE = [
...
'app.middleware.NoTrackingMiddleware',
]
Yet if I place a breakpoint at process_template_response it gets ignored and pub_id is always empty.
Why?

From the documentation (emphasis mine):
process_template_response() is called just after the view has finished executing, if the response instance has a render() method, indicating that it is a TemplateResponse or equivalent.
You state that you're using django.http.shortcuts.render, whose documentation reads:
Combines a given template with a given context dictionary and returns an HttpResponse object with that rendered text.
Django does not provide a shortcut function which returns a TemplateResponse because the constructor of TemplateResponse offers the same level of convenience as render().
Thus render returns a HttpResponse, not a TemplateResponse, and as noted above, process_template_response is only called for TemplateResponses.
You either need to change your view to return TemplateResponse, instead of using the render shortcut, or perform your logic elsewhere. I think your logic could be implemented in a context processor instead of middleware.

Related

how i can set middleware for some views in django?

i have a django project with 50 url (in 1 application).
i would set a custom middleware for 20 urls.
when add middleware to MIDDLEWARE_CLASSES , this set for all urls .
How should this be done?
(i use last version of python and djnago)
You can utilize decorators, view's decorators behave similarly to middlewares, simple ex:
from functools import wraps
def custom_view_decorator(view_function):
#wraps(view_function)
def wrap(request, *args, **kwargs):
# Any preprocessing conditions..etc.
return view_function(request, *args, **kwargs)
return wrap
Usage in views.py:
#custom_view_decorator
def my_view(request, *args, **kwargs):
# view code, return response
you can either add a check condition to your middleware which is not recommended(since you cannot determine whether this middleware is working or not without reading it's code). or create a decorator for those views, decorator job is exactly this.
for starters you could do something like this:
class BlockUrlsMiddleware(MiddlewareMixin):
REDIRECT_URL = reverse("not_allowed")
WHITELISTED_URLS = [
"/your/custom/views/urls",
"/your/custom/apis/urls",
]
def process_request(self, request):
if request.path in self.WHITELISTED_URLS:
return None
else:
return HttpResponseRedirect(self.REDIRECT_URL)
few points to Note:
return None means everything is good and serve this request.
return HttpResponseRedirect means URLs are accessed which are not allowed and block this request by redirecting it to your default URLs (that you need to set) in this case REDIRECT_URL.

Django: redirect url if slug is wrong

I have a function that is run every view to correct slugs.
For example if the slug is /12-post-about-stuff and a user enters /12-post-abot_stof they will be redirected correctly. The problem is that the different views have different url patterns for example:
/posts/post_slug/
...
/posts/post_slug/comments/new
how to I write a function that redirects by fixing the slug name based on the current url?
Edit: I am applying a decorator to every view with a board_name and pk argument. What I don't know is how to dynamically return the new url because the url format is different for each view.
def correct_board_url_name(func):
def wrapper(request, board_slug):
try:
pk = int(board_slug.split('-')[0])
board = Board.objects.get(pk=pk)
if (board.slug != board_slug):
# This does not always work depending on what is entered
return redirect(request.get_full_path().replace(board_slug, board.slug, 1))
else:
return func(request, board_slug)
except:
raise Http404('')
return wrapper
A middleware is a good choice if you want to process requests in many different views.
class RedirectMiddleware(object):
def process_request(self, request):
if request.resolver_match.app_name == 'posts' \
and 'post_slug' in request.resolver_match.kwargs:
new_path = None
# your logic here
if new_path:
return redirect(new_path, permanent=True)
return
In settings:
MIDDLEWARE = [
# another middlewares here ...
'path.to.RedirectMiddleware',
]

Having a global variable available to your templates in Django

I would like to have a common variable available to all my templates. I've been working on this all morning and the only way I can find to do it is to have middleware like this:
class GetNameMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_template_response(self, request, response):
response.context_data['name'] = 'dave'
return response
and have a view like this:
from django.template.response import TemplateResponse
from django.shortcuts import render
def home(request):
return TemplateResponse(request, 'home.html', {})
and then I can access it in the template
Hello {{ name }}
It just seems a bit ugly that I can't use render any more (I know I can do from django.template.response import TemplateResponse as render but that's not really a solution)
(Django 1.10, python 3.5.2)
Django has a standard way for implementing this, which is context processors. This article provides an example implementation
https://www.webforefront.com/django/setupdjangocontextprocessors.html.
Actually a context processor is just a function that accepts a request object and returns a dict with the data you want.

Using middleware to add arguments to view functions in django

I have a pretty small, basic site with a handful of views. Pretty much all of the views perform some interaction with an external API.
In each view I instantiate an object of my API class, which obviously is not very DRY. I also have a piece of middleware that adds one or two useful variables to the request object (my middleware runs last in the chain).
I was thinking of instantiating my api class in the middleware, and then passing it to each view as an argument, i.e.
def my_view(request, my_api_class_instance):
and then calling the view from the middleware, something like:
def process_view(self, request, view_func, view_args, view_kwargs):
my_api = api(some, args, here)
response = view_func(request, my_api, *view_args, **view_kwargs)
return response
It seems like a quick and easy way to tidy some code and reduce repetition. Is there anything inherently BAD about this?
If you look at the Django middleware documentation, you'll see;
If process_view returns an HttpResponse object, Django won’t bother calling any other view or exception middleware, or the appropriate view; it’ll apply response middleware to that HttpResponse, and return the result.
So returning an HttpResponse will skip the execution of other middleware classes below this one which in general should be avoided unless your middleware is the last one in settings.MIDDLEWARE_CLASSES list.
However, you still can bind your API object to HttpRequest instance passed on to middleware. It is the same approach to what AuhenticationMiddleware does in order to populate request.user.
def APIMiddleware(object):
def process_request(self, request):
request.api = API(host, port, user, password, extra={"url": request.get_full_path()})
you can just change view_kwargs in middleware
class SomeMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
view_kwargs['my_api'] = api(some, args, here)
return None
def my_view(request, my_api):
# you can use you api there
def my_view(request, args, my_api)
# you can use you api there
document it's there
middleware returns None, Django will continue processing this request, executing any other process_view() middleware.
but, this only applies to every view function can got the keyword argument 'myapi', otherwise will raise the TypeError.
so the best way isn't pass your api by func arguments, like #ozgur pass your api by request.
You can use a middleware but there are two other possibilities too, both more flexible. The first one is to use a decorator and wrap the view:
#my_api_init_decorator
def my_view(request, my_api):
...
This allows you to explicitly select views, check user authorization or permissions before you init your api...
The second solution is to use class based views and create your own view you inherit from.

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'),
)

Categories

Resources