Django middleware and HttpRequest change - django

I have a middleware to make some calculations/check for each incoming request. Some of view need this calculations result.
As I do not want to call the same code twice, I would like to put results to HttpRequest in middleware, so view will be able to read it.
Could you help me with right hint, how can I add an object to HttpRequest?
thanks

HttpRequest is a normal class, you could directly assign the object to its instance, the request, in the middleware. For example:
class MyMiddleware(object):
def process_request(self, request):
request.foo = 'bar'

You can extend HttpResponse by using so-called "monkey-patch" method. For example you can easily add or replace methods and properties into HttpResponse by calling following function from within your root __init__.py or wsgi.py or even settings.py:
def apply_http_request_patch():
def get_property_value(request):
# return lazily evaluated value
from django.http import HttpRequest
HttpRequest.some_property = property(get_property_value)

Related

How to call an auth view from another view?

I want to execute a custom logic block in a view before the PasswordResetView is called. This is what I am trying to do, which of course always fails regardless the code, I must be taking a non adequate route to achieving this. I need to save some information, do some internal notifications and only call the password reset view if a condition is met.
views.py
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(template_name = 'account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done'))
Which is the standard way of achieving this?
Many thanks in advance.
You can call the function that is returned by the .as_view():
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(
template_name='account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done')
)(request)
That being said, it might make more sense to just subclass the PasswordResetView, and for example overwrite the .dispatch(..) method.

Django. 301 Redirect from old URL to new

Hello! Please tell me how to organize a redirect correctly.
There is an old version of the site and a new one. In the old version (another CMS, not Django) the objects have their own URL, in the new revised scheme, and the objects have a different URL.
In each object on the new site, there is a completed field with the old URL. In model.py it looks like this:
old_url = models.CharField('Old URL', blank=True, max_length=100)
I specifically moved the old url to a separate field. Or was it not necessary to do this?
Question. How to set up a redirect correctly, so that after going to the object using the old URL, the site visitor will be redirected to the new URL of this object?
IMHO, I don't think writting old_url for each and every object is pretty inefficient. Instead you can implement a custom 404 view, and handle the redirection there.
I think you can create some regex or plain url maps to new url and redirect accordingly.
import re
from django.http import HttpResponseNotFound
OLD_URL_MAP = { 'old_url_regex': 'new_url_path'}
def handler404(self, request):
for old_re, new_url in OLD_URL_MAP.items():
if re.match(old_re, request.path):
return redirect(new_url, request.resolver_match.kwargs)
return HttpResponseNotFound('not found')
# inside urls.py
handler404 = 'myapp.views.handler404'
Here I have used a map hard coded in python, you can create a model for that as well.
Update
A costly solution is to use middleware. You can try like this:
import re
from django.urls import resolve
OLD_URL_MAP = { 'old_url_regex': 'new_url_path'}
class RerouteMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
resolve(request.path_info) # trying to find if the current url exists in your django project. if not, it will throw exception.
except:
for old_re, new_url in OLD_URL_MAP.items(): # iterating through urls
if re.match(old_re, request.path):
return redirect(new_url, request.resolver_match.kwargs)
response = self.get_response(request)
return response
And add that middleware at the bottom of MIDDLEWARE settings.
FYI, its a regex based solution, assuming those urls are dynamic. Instead you can use plain text urls, but its up to you.
Use redirect() from django.shortcuts [the same library from where you import render]. Also, assuming, that the old_url contains only the relative url.
from django.shortcuts import render, redirect
def someView(request):
q = ~some queryset returning the current object~
current_url = request.get_full_path().strip("http://www.example.com/")
if q.old_url == current_url:
redirect(q.new_url)
else:
pass
Remember, redirect() returns an HttpResponse.

Bypass decorator with mock in django test

I am trying to write a simple test however my views are decorated with nested user_passes_test statements. They check things like a stripe subscription and is_authenticated. I have found various posts such as this which address how to bypass a decorator with patch but I can't quite work out how to integrate everything together.
tests.py
#patch('dashboard.views.authorised_base_user_checks', lambda func: func)
def test_dashboard_root_exists(self):
response = self.client.get('/dashboard/')
self.assertEqual(200, response.status_code)
decorator in views
def authorised_base_user_checks(view_func):
decorated_view_func = login_required(user_active(subscriber_exists(subscriber_valid(view_func))))
return decorated_view_func
views.py
#authorised_base_user_checks
def IndexView(request):
...
The above still fails to pass through the decorator.
Thanks!
This approach with patching of decorator most probably does not work because import of views module happens after the patching. If view has been already imported the decorator had been already applied to IndexView and patching the decorator function would have no effect at all.
You can reload the view module to overcome this:
import imp
import dashboard.views
#patch('dashboard.views.authorised_base_user_checks', lambda func: func)
def test_dashboard_root_exists(self):
# reload module to make sure view is decorated with patched decorator
imp.reload(views)
response = self.client.get('/dashboard/')
self.assertEqual(200, response.status_code)
# reload again
patch.stopall()
imp.reload(views)
Disclaimer: this code only demonstrates the idea. You need to make sure stopall and final reload always happens, so they should be in finally or in tearDown.

Django: How to provide context to all views (not templates)?

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

Redirect Middleware not working - Django

I created a simple redirect middleware
class RedirectMiddleware(object):
def urlredirect(self, request):
path = self.request.build_absolute_uri()
if "something" in path:
URL = "http://www.someurl.com"
else:
URL = "http://www.otherurl.com"
return HttpResponsePermanentRedirect(URL)
But i doesnt seem to work , it doesnt does anything , and yes i added it on the settings.py, any idea ?
You need to implement process_request
Writing your own middleware is easy. Each middleware component is a
single Python class that defines one or more of the following methods:
process_request
process_request(request)
request is an HttpRequest object.
process_request() is called on each request, before Django decides
which view to execute.
It should return either None or an HttpResponse object. If it returns
None, Django will continue processing this request, executing any
other process_request() middleware, then, process_view() middleware,
and finally, the appropriate view. If it returns an HttpResponse
object, Django won’t bother calling any other request, view or
exception middleware, or the appropriate view; it’ll apply response
middleware to that HttpResponse, and return the result.