Custom 403 error page in django rest framework - django

I am trying to override the default 403.html template of django rest framework, by declaring in ulrs.py handler403 = 'my_app.views.handler403'.
And in the app's views.py:
def handler403(request, exception, template_name='403.html'):
response = render_to_response('403.html', {})
response.status_code = 403
return response
The template's directory is included in TEMPLATE_DIRS in settings.py.
However, making a request to an endpoint that has IsAdminUser permission, renders the default drf template.
The same exact procedure for the 404 exception works perfectly fine.
Any answer I saw in the web did not help me resolve the issue.

It's quite simple actually:
You have to overwrite 'custom_exception_handler' of the DRF just like below:
from Django.shortcuts import render_to_response
def custom_exception_handler(...):
response = render_to_response('path/to/template/403.html', {})
response.status_code = 403
return response

It does not work because DRF returns a json response, does not render a template.

try this:
from django.shortcuts import render
def handler403(request, exception, template_name='403.html'):
return render(request, '403.html')

Related

Correct way of implementing custom 404 page in Django 3 in production

I’ve tried importing and editing handler404 in urls.py, made sure pointed to right template etc but kept getting server 500 error responses. The only way I could get it to work was changing return render( to return HttpResponseNotFound( but in that case I only get the text representation of ‘mysite.views.404_error.html’ as need to return HTML directly with HttpResponseNotFound. Wondering what is the correct way to return a custom 404 error template. Thanks a lot.
We can render the template with render_to_string(…) and then wrap the result in a HttpResponseNotFound:
from django.http import HttpResponseNotFound
from django.template.loader import render_to_string
def my_404_handler(request, exception, template_name='404.html'):
context = {} # some context
content = render_to_string(
'some-template-name.html',
context,
request
)
return HttpResponseNotFound(content)
We here thus render a template some-template-name.html, and the content; the result of the function, is then returned with a HttpResponseNotFound.
For me the only solution that worked (Django 3.7) dev/prod is here
Please see #elano7 answer

Django Test Client client.post() sends GET request?

I am new to Python and Django. I did a experiment on enforcing request method (e.g. for certain url you can only use GET). Here is my code.
tests.py
from django.test import TestCase, Client
client = Client()
class MyTests(TestCase):
def test_request_method:
""" Sending wrong request methods should result in 405 error """
self.assertEqual(client.post('/mytest', follow = True).status_code, 405)
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name = 'index'),
url(r'^mytest/', views.mytest, name = 'mytest'),
]
views.py
from django.http import HttpResponse
def mytest(request):
if request.method == 'GET':
return HttpResponse("Not implemented", status = 500)
else:
return HttpResponse("Only GET method allowed", status = 405)
But the test always returns status 500.
I saw here that this may be related to using follow=True in the client.post() call. However if I use follow=False I will get status 301 instead.
Any ideas? Thank you!
Does it perhaps redirect /mytest to /mytest/? The documentation suggests that by default, a trailing slash is added by doing a redirect if no URL pattern matches without the slash, and to quote:
Note that the redirect may cause any data submitted in a POST request to be lost.
A request caused by commonly used redirect status codes is always a GET request. You could either make the request to /mytest/ or remove the trailing slash from your URL pattern.

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.

Why does the login_required decorator return a 302 status code?

I have a view that allows to add tags dynamically with an ajax request. It looks like this:
#require_POST
#login_required
def addtag(request):
"""
a view to create a new tag in the tag database
"""
some logic here
This is what my url.py looks like:
urlpatterns = patterns('',
url(r'^addtag/$',addtag, name='addtag'),
)
And my test is doing that:
def test_addtag(self):
url='^addtag/$'
response = self.client.post(url,{'addtag':'"new tag"'})
self.assertEqual(response.status_code,401)
I expected the returned status code to be a 401, since the testclient is not logged in. So the first decorator, checking if the request is a post is being passed happily. Then i expected the login_required decorator to return a 401, but it didn't:
AssertionError: 302 != 401
First i thought the login_required decorator would be redirecting to some login page. Checked that, i do not have a settings.LOGIN_REDIRECT_URL specified. So what is login_required doing in this case?
#login_required redirects to the login page if the user is not logged in -- hence the view returns 302 in such a case. (If you did not set LOGIN_REDIRECT_URL it uses a default value.)

How to display a custom error page for HTTP status 405 (method not allowed) in Django when using #require_POST

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