Django logout not working - django

I seem to have the same problem as in this question: Django logout problem
Mine is a bit weirder, it works in google chrome.... but not in firefox...
this is my logout function: (in views.py)
def my_logout(request):
logger.debug("Logout called by user")
try:
# Here I do some custom stuff, like logging this action in a database and so on
# For this question it shouldn't matter... because in a try catch
# so whatever goes wrong here, logging out should work anyway
except Exception, e:
logger.info("Logging logout action error: %s" % e)
logout(request)
return HttpResponseRedirect("/")
in settings.py I have:
LOGIN_URL = '/desktop/login/'
LOGOUT_URL = '/desktop/logout/'
LOGIN_REDIRECT_URL = '/'
And in the urls.py of the app iamapps (include in the project urls as /desktop/):
url(r'^login/$', 'iamapps.views.my_login', name='iamapps.login'),
url(r'^logout/$', 'iamapps.views.my_logout', name='iamapps.logout'),
further info:
django 1.4.3 (just updated from 1.3 to 1.4 ....)
python 2.7
works in Chrome but not in Firefox 17.0.1, Linux
The fact that it does work in google chrome but does not work in firefox puzzles me the most. Seems it has something to do with firefox keeps remembering the user as being logged on...
EDIT:
I get a broken pipe.... but I seem to get it not on logging out... but going to the home view after logout....
Traceback (most recent call last):
File "/usr/lib/python2.7/wsgiref/handlers.py", line 86, in run
self.finish_response()
File "/usr/lib/python2.7/wsgiref/handlers.py", line 127, in finish_response
self.write(data)
File "/usr/lib/python2.7/wsgiref/handlers.py", line 210, in write
self.send_headers()
File "/usr/lib/python2.7/wsgiref/handlers.py", line 268, in send_headers
self.send_preamble()
File "/usr/lib/python2.7/wsgiref/handlers.py", line 192, in send_preamble
'Date: %s\r\n' % format_date_time(time.time())
File "/usr/lib/python2.7/socket.py", line 324, in write
self.flush()
File "/usr/lib/python2.7/socket.py", line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 58684)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 582, in process_request_thread
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 323, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/media/storage/django/sites/iamfloraservice/parts/django/django/core/servers/basehttp.py", line 139, in __init__
super(WSGIRequestHandler, self).__init__(*args, **kwargs)
File "/usr/lib/python2.7/SocketServer.py", line 641, in __init__
self.finish()
File "/usr/lib/python2.7/SocketServer.py", line 694, in finish
self.wfile.flush()
File "/usr/lib/python2.7/socket.py", line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe
----------------------------------------
[24/Dec/2012 14:33:25] "GET / HTTP/1.1" 200 48247
Edit 2
it goes to this view after loging out and being redirected:
def home(request, template='iamfloraservice/home.html'):
logger.debug("Home view called by user %s" % request.user)
return render_to_response(template,{},context_instance=RequestContext(request))
I think the redirect with the request to this view causes the problem....
In the log sais it's still user 'michel' (beacuse the view uses the request from the redirect, and that had user michel)... however... user michel is being logged out in the mean time....
EDIT 3
because of the suggestion it's due to the logger.
unremarking the logging does not help
And it's the default logger:
import logging
logger = logging.getLogger(__name__)
EDIT 4 (30-12-2012)
My logout is from a main window where I show a logout link when a user is logged on and a login link if a user is logged out. Also it contains a toolbar, which tools are filled depending on the the user and it's membership of groups.
I think the problem is, it's reloading this main window while the cache and the user in it's request isn't cleared yet. Somehow Chrome knows how to handle this, and firefox results in a broken pipe error. Clearing the cache manually in the browser results in the correct view after reload....
a solution might be to redirect to a page without anything in it that contains users...
or find out to clear the cache on the right moment myselve....
this problem describes maybe the same... but I cannot expect users to do anything in the browser just to logout? see django 1.4 caching GET to /login/
Edit 5 (31-12-2012)
It seems it's a caching problem.... but not sure how to fix this yet.
these are my caching settings:
if not DEBUG:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
else:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
But I tried without the dummycache as well
EDIT 6 (4-jan-2013)
Still no solution.... I changed the way I log out to the django way, and I Am using the signals now... see my own answer below.
but still it gives the brokenpipe error which causes firefox stopping logging out. It's not a caching problem. If I go to another page, or even worse... the admin pages. I Am still logged on. The only way to logout is through the logout on the admin page.... If it's not an admin user... there is no way to get me logged out on the firefox browser.
When logging off using the admin interface, so the signal works fine...
I checked by turning off the signal.... and still the logging out in firefox does not work.
Conclusion: going back to the main page () is causing the problem.
EDIT 7 (4 jan 2013)
I made a simple loggedout view for testing,
this template:
<html>
<head>
<title>
Logged out
</title>
</head>
<body>
You are succesfully logged out.<br>
<br>
Go back to the main page
or<br>
log in again
</body>
</html>
and the logged out view:
class LoggedOutView(TemplateView):
template_name = "iamapps/logged_out.html"
and changed the urls in to:
url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/desktop/loggedout/'}, name='iamapps.logout'),
#url(r'^logout/$', 'django.contrib.auth.views.logout_then_login', name='iamapps.logout'),
url(r'^loggedout/$', LoggedOutView.as_view(),name='iamapps.loggedout'),
and still, to simplyfy things... I have the signals turned off.
and it's still not working in firefox.... but it works in chrome
In firefox it does not go to the logged out page

I generally just use the contributed view to logout users. Simply add this to your root urls.py file:
# Would be nice to use settings.LOGIN_URL for `next_page` here, too
url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/login'}),
and you'll be good to go.
Happy Djangoing.

Finally I totally switched to almost back to django defaults...
Using:
in views.py:
from django.contrib.auth.forms import AuthenticationForm
from django.views.generic.edit import FormView
class LoginView(FormView):
"""
This is a class based version of django.contrib.auth.views.login.
"""
form_class = AuthenticationForm
redirect_field_name = REDIRECT_FIELD_NAME
template_name = 'iamapps/login.html'
#method_decorator(csrf_protect)
#method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
return super(LoginView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
"""
The user has provided valid credentials (this was checked in AuthenticationForm.is_valid()). So now we
can check the test cookie stuff and log him in.
"""
self.check_and_delete_test_cookie()
login(self.request, form.get_user())
return super(LoginView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(LoginView, self).get_context_data(**kwargs)
apps_settings=iamapps_settings()
if apps_settings[LOGON_BASE_APP_NAME]:
self.extend_template="%s/base.html" % apps_settings[LOGON_BASE_APP_NAME]
else:
self.extend_template="iamapps/base.html"
context['extend_template']=self.extend_template
return context
def form_invalid(self, form):
"""
The user has provided invalid credentials (this was checked in AuthenticationForm.is_valid()). So now we
set the test cookie again and re-render the form with errors.
"""
self.set_test_cookie()
return super(LoginView, self).form_invalid(form)
def get_success_url(self):
if self.success_url:
redirect_to = self.success_url
else:
redirect_to = self.request.REQUEST.get(self.redirect_field_name, '')
netloc = urlparse.urlparse(redirect_to)[1]
if not redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL
# Security check -- don't allow redirection to a different host.
elif netloc and netloc != self.request.get_host():
redirect_to = settings.LOGIN_REDIRECT_URL
return redirect_to
def set_test_cookie(self):
self.request.session.set_test_cookie()
def check_and_delete_test_cookie(self):
if self.request.session.test_cookie_worked():
self.request.session.delete_test_cookie()
return True
return False
def get(self, request, *args, **kwargs):
"""
Same as django.views.generic.edit.ProcessFormView.get(), but adds test cookie stuff
"""
self.set_test_cookie()
return super(LoginView, self).get(request, *args, **kwargs)
and urls:
url(r'^login/$', LoginView.as_view(), name='login'),
This solved all my troubles... about loggin and logiing off on...
the login and logout signals just working fine:
from django.contrib.auth.signals import user_logged_out, user_logged_in
# Note, these login and logout signals are registered in imamstats views
def iam_logged_out_actions(sender, user, request, **kwargs):
try:
# ... do my logging out actiosn (stats etc.)
except Exception, e:
logger.error("Logging logout action error: %s" % e)
# Note, these login and logout signals are registered in imamstats views
def iam_logged_in_actions(sender, user, request, **kwargs):
try:
# ... do my log in stats etc. things
except Exception, e:
logger.error("Logging login action error: %s" % e)

browsing to stackoverflow... (did another search)
I found this one: .... see django.contrib.auth.logout in Django ..
But is is even worse.... I found this... astonished... but explains it all:
Django, Logout_URL doesn't redirect well
The I found out it's a won't fix bug (###$%%) not allowed to curse on christmas eve....
So the solution to do my custom stuff is in the signals in stead of using my own view.
Do the default view and redirect...
and use this documentation to create a signal.. https://docs.djangoproject.com/en/dev/topics/auth/#login-and-logout-signals
Adding the signals is quite easy, i put it in models.py off my main app (iamapps):
import logging
from django.contrib.auth.signals import user_logged_out
from django.contrib.auth.signals import user_logged_in
logger = logging.getLogger(__name__)
def iam_logged_out_actions(sender, user, request, **kwargs):
#whatever...
logger.debug("Logging out: user = %s" % user)
user_logged_out.connect(iam_logged_out_actions)
def iam_logged_in_actions(sender, user, request, **kwargs):
#whatever...
logger.debug("Logging in: user = %s" % user)
user_logged_in.connect(iam_logged_in_actions)
This works....however it does not solve the broken pipe which I think might cause the failure on the logging out... so logging out in firefox still fails... and in chrome it works...
Logging out from django admin page works in firefox.. and the signal has another pro: also from logging out in the admin interface, the signal is being called...
for logging out i use now this urls.py:
url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='iamapps.logout'),

My suspicion is that the original logout view returns a response that clears cookie or something like that, and you're throwing that response out. Could you try simply returning its response to the user, like this?
def my_logout(request):
# something...
return logout(request)

The verified answer work well for the mentioned version but it is not working in django 2.2 version any more. so in order to do successful redirect after logout you have to define the attributes.
from django.contrib import admin
from django.urls import path, include
from dark_bot import views
from django.contrib.auth import views as v
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index, name="index"),
path('contact/', views.contact, name="contact"),
path('search/', include('search.urls')),
path('user/', include('accounts.urls')),
path('dashboard/', include('dashboard.urls')),
path('accounts/login/', v.LoginView.as_view(), name="login"),
path('accounts/logout/',v.LogoutView.as_view(next_page='/'),name="logout")
# path('dashboard/', include('dashboard.urls')),
]
This is how you can see I passed the next_page attribute to the class LogoutView which will tell where to redirect or go after successfull logout.
Hope this may help someone.
Happy Coding!

Related

Only allow django admin on site id 1

I want to only show the admin for my site with id 1.
In other views I've defined this in the dispatch, but I haven't found a way to do this for the admin.
I tried making a custom admin class, but I didn't see a way to do this either.
Is there a way to define dispatch for admin, or limiting the sites where an admin shows?
The dispatch I'm using:
def dispatch(self, *args, **kwargs):
try:
if get_current_site(self.request).pk != settings.MY_SITE_ID:
response = render(self.request, 'my_app/404.html')
response.status_code = 404
return response
except Site.DoesNotExist:
response = render(self.request, 'my_app/404.html', {'site_doesnotexist': True})
response.status_code = 404
return response
return super(MyView, self).dispatch(*args, **kwargs)
You can specify a different ROOT_URLCONF in the Settings file for the site with admin enabled. This ROOT_URLCONF would include the admin url patterns.
MY_SITE_ID = 1
ROOT_URLCONF = 'my_project.urls_with_admin'
# my_project/my_project/urls_with_admin.py
from django.conf.urls import url
from django.contrib import admin
from urls import urlpatterns
urlpatterns.append(url(r'^admin/', admin.site.urls))
The base urls.py file, used by the other sites, would omit the admin patterns, making the admin inaccessible.

Selenium / Django gives Foreign Key error

I am trying to run functional tests using Selenium for a Django project. On both Firefox and Chrome I am getting a weird Foreign Key error when I try to test that a superuser can change a normal user's status to staff (I assume this is all verified via Django's internal testing, but thought it would be good practice to include it in my app's testing since my user scenarios depend on the functionality). It almost looks like Django doesn't like Selenium saving anything to the database? This one error trickles down to my other tests, too, so it seems like something breaks behind the scenes--like Selenium loses its database connection. Does anyone know why this happens?? I'm willing to dump the test in favor of assuming that the functionality works, but would love to fix this and use the test.
One possible symptom is that in Chrome, between between the outputs of "checked the box" and "clicked the save button", I get a broken pipe (does not happen in Firefox).
It seems to break around here, whenever Selenium clicks the 'save' button:
# Form looks properly rendered, now click the 'Staff status'
# checkbox and submit it
isStaffCheckbox = self.browser.find_element_by_id('id_is_staff')
isStaffCheckbox.click()
print 'checked the box'
# Save the form
saveBtn = self.browser.find_element_by_css_selector('input[value="Save"]')
saveBtn.click()
print 'clicked the save button'
The error message I get is:
Traceback (most recent call last):
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 268, in __call__
self._post_teardown()
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 533, in _post_teardown
self._fixture_teardown()
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 553, in _fixture_teardown
skip_validation=True, reset_sequences=False)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/__init__.py", line 161, in call_command
return klass.execute(*args, **defaults)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/base.py", line 255, in execute
output = self.handle(*args, **options)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/base.py", line 385, in handle
return self.handle_noargs(**options)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/commands/flush.py", line 82, in handle_noargs
emit_post_sync_signal(set(all_models), verbosity, interactive, db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/sql.py", line 195, in emit_post_sync_signal
interactive=interactive, db=db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 170, in send
response = receiver(signal=self, sender=sender, **named)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/contrib/auth/management/__init__.py", line 96, in create_permissions
auth_app.Permission.objects.using(db).bulk_create(perms)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 444, in bulk_create
self._batched_insert(objs_without_pk, fields, batch_size)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 902, in _batched_insert
using=self.db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/manager.py", line 215, in _insert
return insert_query(self.model, objs, fields, **kwargs)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 1661, in insert_query
return query.get_compiler(using=using).execute_sql(return_id)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 937, in execute_sql
cursor.execute(sql, params)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 122, in execute
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 120, in execute
return self.cursor.execute(query, args)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/MySQL_python-1.2.4b4-py2.7-macosx-10.8-intel.egg/MySQLdb/cursors.py", line 202, in execute
self.errorhandler(self, exc, value)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/MySQL_python-1.2.4b4-py2.7-macosx-10.8-intel.egg/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`test_videos2002`.`auth_permission`, CONSTRAINT `content_type_id_refs_id_d043b34a` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`))')
=====
Updated with code
Also note that test_admin_can_make_a_user_staff causes my other tests to Error out with the same error--but they are okay when I take out the "save" command from that test.
I guess two things I really don't understand are: 1) Why this happens with the built-in Django admin view (thought it should just work), and 2) Why one test error cascades through to my other tests? I thought they were independent.
Thanks for looking at this!
From my functional_tests.test.py (borrowed from https://github.com/lincolnloop/django-selenium-intro/tree/master/selenium_intro):
from django.test import LiveServerTestCase
class SeleniumTestCase(LiveServerTestCase):
"""
A base test case for selenium, providing hepler methods for generating
clients and logging in profiles.
"""
def open(self, url):
self.browser.get("%s%s" % (self.live_server_url, url))
From my admin_django.py (set of test cases)
from functional_tests.test import SeleniumTestCase
from selenium.webdriver.common.keys import Keys
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.test import LiveServerTestCase
from django.conf import settings
from selenium import webdriver
class AdminDjango(SeleniumTestCase):
def setUp(self):
User.objects.create_superuser(username='vcb',
password='rock5!',
email='me#name.edu')
User.objects.create_user(username='teaching',
password='assistant',
email='ta#name.edu')
# self.browser = webdriver.Chrome(settings.SELENIUM_WEBDRIVER)
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3)
self.browser.set_page_load_timeout(10)
def tearDown(self):
self.browser.quit()
def check_for_links(self, link_text):
"""
Helper function to check links on a page for certain text
"""
links = self.browser.find_elements_by_tag_name('a')
self.assertTrue(link_text, [link.text for link in links])
def admin_logs_in(self):
"""
Helper function that logs the admin user into the page
"""
username_field = self.browser.find_element_by_name('username')
username_field.send_keys('vcb')
password_field = self.browser.find_element_by_name('password')
password_field.send_keys('rock5!')
password_field.send_keys(Keys.RETURN)
def admin_log_in_complete(self):
"""
Includes navigation to the admin page
"""
self.open('/admin/')
self.admin_logs_in()
def test_admin_can_login(self):
"""
Admin user can log into the Django admin interface
"""
self.open('/admin/')
body = self.browser.find_element_by_tag_name('body')
self.assertIn('VCB Administration', body.text)
self.admin_logs_in()
# her username and password are accepted, and she is taken to
# the Site Administration page
body = self.browser.find_element_by_tag_name('body')
self.assertIn('Site administration', body.text)
def test_admin_page_renders_properly(self):
"""
The admin page should have at least two fields:
- Users
- Classes
Admins may have to add staff status to users, and they may have to
adjust the information for a class
"""
self.admin_log_in_complete()
self.check_for_links('Users')
self.check_for_links('Groups')
self.check_for_links('Classess')
def test_admin_can_make_a_user_staff(self):
"""
Admin users can add staff status to existing users
"""
self.admin_log_in_complete()
pageLinks = self.browser.find_elements_by_tag_name('a')
for link in pageLinks:
if link.text == 'Users':
userLink = link
userLink.click()
headers = self.browser.find_elements_by_tag_name('h1')
self.assertTrue('Select user to change',
[header.text for header in headers])
users = self.browser.find_elements_by_xpath('//table[#id="result_list"]/tbody/tr/th/a')
self.fail('Finish writing the test!')
# rowCount = 1
# for user in users:
# xpath = '//table[#id="result_list"]/tbody/tr[' + str(rowCount) + ']/td[5]/img'
# # check first that this user is not a staff
# staffIcon = self.browser.find_element_by_xpath(xpath)
# isStaff = staffIcon.get_attribute('alt')
#
# if isStaff == 'false':
# user.click()
# userHeaders = self.browser.find_elements_by_tag_name('h1')
# self.assertTrue('Change user',
# [userHeader.text for userHeader in userHeaders])
#
# # Are the right fields present in the user's form?
# formHeaders = self.browser.find_elements_by_tag_name('h2')
# self.assertTrue('Personal info',
# [formHeader.text for formHeader in formHeaders])
# self.assertTrue('Permissions',
# [formHeader.text for formHeader in formHeaders])
# self.assertTrue('Important dates',
# [formHeader.text for formHeader in formHeaders])
#
# # Form looks properly rendered, now click the 'Staff status'
# # checkbox and submit it
# isStaffCheckbox = self.browser.find_element_by_id('id_is_staff')
# isStaffCheckbox.click()
# print 'checked the box'
# # Save the form
# saveBtn = self.browser.find_element_by_css_selector('input[value="Save"]')
# saveBtn.click()
# print 'clicked the save button'
# # Returns you to the admin page
# messageBox = self.browser.find_element_by_class_name('info')
# self.assertIn('successfully', messageBox.text)
#
# # Check that staff status changed
# staffIcon = self.browser.find_element_by_xpath('//table[#id="result_list"]/tbody/tr[' + str(rowCount) + ']/td[5]/img')
# isStaff = staffIcon.get_attribute('alt')
# self.assertTrue(isStaff)
# print 'should now be staff'
# rowCount += 1
def test_admin_can_change_a_class_obj_bank_id(self):
"""
Admin users can change a class's objective bank id
"""
self.fail('Finish writing the test!')
def test_logging_out_redirects_to_login_page(self):
"""
Logging out of the admin page should redirect to the main page
"""
self.admin_log_in_complete()
logOut = self.browser.find_element_by_link_text('Log out')
logOut.click()
body = self.browser.find_element_by_tag_name('body')
self.assertIn('VCB Administration', body.text)
As I mentioned in the comments, this seems to be a documented bug in Django. Here and here are the official bug reports. As reported in the second link, one workaround is to swap the order of django.contrib.auth and django.contrib.contenttypes in INSTALLED_APPS, as such:
What originally is this:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'testapp'
)
Should become:
INSTALLED_APPS = (
'django.contrib.contenttypes',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'testapp'
)

Django raising 404 with a message

I like to raise 404 with some error message at different places in the script eg: Http404("some error msg: %s" %msg)
So, in my urls.py I included:
handler404 = Custom404.as_view()
Can anyone please tell me how should I be handling the error in my views. I'm fairly new to Django, so an example would help a lot.
Many thanks in advance.
Generally there should not be any custom messages in 404 errors bu if you want to implement it you can do this using django middlewares.
Middleware
from django.http import Http404, HttpResponse
class Custom404Middleware(object):
def process_exception(self, request, exception):
if isinstance(exception, Http404):
# implement your custom logic. You can send
# http response with any template or message
# here. unicode(exception) will give the custom
# error message that was passed.
msg = unicode(exception)
return HttpResponse(msg, status=404)
Middlewares Settings
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',
'college.middleware.Custom404Middleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
This will do the trick. Correct me if I am doing any thing wrong. Hope this helps.
In general, 404 error is "page not found" error - it should not have customizable messages, simply because it should be raised only when a page is not found.
You can return a TemplateResponse with status parameter set to 404
Raise an Http404 exception inside a view. It's usually done when you catch a DoesNotExist exception. For example:
from django.http import Http404
def article_view(request, slug):
try:
entry = Article.objects.get(slug=slug)
except Article.DoesNotExist:
raise Http404()
return render(request, 'news/article.html', {'article': entry, })
Even better, use get_object_or_404 shortcut:
from django.shortcuts import get_object_or_404
def article_view(request):
article = get_object_or_404(MyModel, pk=1)
return render(request, 'news/article.html', {'article': entry, })
If you'd like to customize the default 404 Page not found response, put your own template called 404.html to the templates folder.
Yes we can show specific exception message when raise Http404.
Pass some exception message like this
raise Http404('Any kind of message ')
Add 404.html page into templates directory.
templates/404.html
{{exception}}
I figured out a solution for Django 2.2 (2019) after a lot of the middleware changed. It is very similar to Muhammed's answer from 2013. So here it is:
middleware.py
from django.http import Http404, HttpResponse
class CustomHTTP404Middleware:
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.
return response
def process_exception(self, request, exception):
if isinstance(exception, Http404):
message = f"""
{exception.args},
User: {request.user},
Referrer: {request.META.get('HTTP_REFERRER', 'no referrer')}
"""
exception.args = (message,)
Also, add this last to your middleware in settings.py: 'app.middleware.http404.CustomHTTP404Middleware',
if you want to raise some sort of static messages for a particular view , you can do as follows:-
from django.http import Http404
def my_view(request):
raise Http404("The link seems to be broken")
You can return a plain HttpResponse object with a status code (in this case 404)
from django.shortcuts import render_to_response
def my_view(request):
template_context = {}
# ... some code that leads to a custom 404
return render_to_response("my_template.html", template_context, status=404)
In my case, I wanted to take some action (e.g. logging) before returning a custom 404 page. Here is the 404 handler that does it.
def my_handler404(request, exception):
logger.info(f'404-not-found for user {request.user} on url {request.path}')
return HttpResponseNotFound(render(request, "shared/404.html"))
Note that HttpResponseNotFound is required. Otherwise, the response's HTTP status code is 200.
The default 404 handler calls 404.html . You could edit that if you don't need anything fancy or can override the 404 handler by setting the handler404 view -- see more here

Monkey patched django auth's login, now its tests fail

My app seeks to wrap the django.contrib.auth.views login and logout views with some basic auditing/logging capabilities. I'm following the prescription as described in the django-axes project, and in running on a server and some other tests, it works as expected, transparently without issue.
The code goes like this:
from django.contrib.auth import views as auth_views
from myapp.watchers import watch_login
class WatcherMiddleware(object):
def __init__(self):
auth_views.login = watch_login(auth_views.login)
And
def watch_login(func):
def decorated_login(request, *args, **kwargs):
#do some stuff
response = func(request, *args, **kwargs)
#more stuff
return response
return decorated_login
Urls:
#Edit: Added project's urls - just using vanilla django's auth login
(r'^accounts/login/$', 'django.contrib.auth.views.login',{"template_name":settings.LOGIN_TEMPLATE }),
However, in our build workflow, we run into some issues in the django.contrib.auth.tests.views.
Specifically, these are the tests that fail in django.contrib.auth:
ERROR: test_current_site_in_context_after_login (django.contrib.auth.tests.views.LoginTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python26\lib\site-packages\django\contrib\auth\tests\views.py", line 192, in test_current_site_in_context_after_login
response = self.client.get(reverse('django.contrib.auth.views.login'))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 351, in reverse
*args, **kwargs)))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 297, in reverse
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'myapp.watchers.decorated_login' with arguments '()' and keyword arguments '{}' not found.
======================================================================
ERROR: test_security_check (django.contrib.auth.tests.views.LoginTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python26\lib\site-packages\django\contrib\auth\tests\views.py", line 204, in test_security_check
login_url = reverse('django.contrib.auth.views.login')
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 351, in reverse
*args, **kwargs)))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 297, in reverse
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'myapp.watchers.decorated_login' with arguments '()' and keyword arguments '{}' not found.
Only these two tests fail with the inclusion of the wrapped login monkey patch.
It seems like the reverse() call in the django auth test behaves differently than how an unpatched function does its thing.
The reason why we're going this route for wrapping logging vs. using django 1.3's new authentication signals is because the logging method provided there only tells you if a wrong attempts happens - it doesn't give you access to the request object to log additional information around that improper request. Patching the authentication form in that case would not have been helpful, hence our need to wrap the login function.
Am I doing something wrong with my wrap of the login function? Or is this as to be expected with tests failing due to other side effects, despite no change in overall functionality?
edit: I'm running python 2.6.4, django 1.2.5
Couldn't you simply wrap it in another view?
urls:
url(
r'^accounts/login/$',
'accounts.views.login',
{"template_name":settings.LOGIN_TEMPLATE }
),
accounts.views:
from django.contrib.auth import views as auth_views
def login(request, *args, **kwars):
# do some stuff
response = auth_views.login(request, *args, **kwars)
# more stuff
return response
Like this, django.contrib.auth.tests will be testing the view which they were written for and you can write your own tests for the "more stuff" you need.
I suspect this is the same underlying issue that affects django-registration in that the test runner only imports the URLs of the app being tested at the time -- ie, contrib.auth and not myapp There are various tickets about things similar to this issue but a quick scan of them implies the solution is to decouple things, which isn't going to be viable for your solution I'm guessing.
Another way around it would be to use Fabric file or Makefile to trigger a subset of tests, avoiding the two that fail because of your monkeypatch, and then add two alternate ape-friendly tests to replace them.

Can login_required by applied to an entire app?

Is there a way I can apply the login_required decorator to an entire app? When I say "app" I mean it in the django sense, which is to say a set of urls and views, not an entire project.
Yes, you should use middleware.
Try to look through solutions which have some differences:
http://www.djangosnippets.org/snippets/1179/ - with list of exceptions.
http://www.djangosnippets.org/snippets/1158/ - with list of exceptions.
http://www.djangosnippets.org/snippets/966/ - conversely with list of login required urls.
http://www.djangosnippets.org/snippets/136/ - simplest.
As of Django 3+, you can set login_require() to an entire app by applying a middleware. Do like followings:
Step 1: Create a new file anything.py in your yourapp directory and write the following:
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
//for registering a class as middleware you at least __init__() and __call__()
//for this case we additionally need process_view() which will be automatically called by Django before rendering a view/template
class ClassName(object):
//need for one time initialization, here response is a function which will be called to get response from view/template
def __init__(self, response):
self.get_response = response
self.required = tuple(re.compile(url) for url in settings.AUTH_URLS)
self.exceptions = tuple(re.compile(url)for url in settings.NO_AUTH_URLS)
def __call__(self, request):
//any code written here will be called before requesting response
response = self.get_response(request)
//any code written here will be called after response
return response
//this is called before requesting response
def process_view(self, request, view_func, view_args, view_kwargs):
//if authenticated return no exception
if request.user.is_authenticated:
return None
//return login_required()
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
//default case, no exception
return None
Step 2: Add this anything.py to Middleware[] in project/settings.py like followings
MIDDLEWARE = [
// your previous middleware
'yourapp.anything.ClassName',
]
Step 3: Also add the following snippet into project/settings.py
AUTH_URLS = (
//disallowing app url, use the url/path that you added on mysite/urls.py (not myapp/urls.py) to include as your app urls
r'/your_app_url(.*)$',
)
I think you are looking for this snippet, containing login-required middleware.
This is an old question. But here goes:
Django Decorator Include
This is a substitute of include in URLConf. Pefect for applying login_required to an entire app.
I clicked all the links in the anwsers, but they were all based on some kind of regular expressions. On Django 3+ you can do the following to restrict for a specific app:
Declare app_name="myapp" in your app's urls.py (https://docs.djangoproject.com/en/3.2/intro/tutorial03/#namespacing-url-names)
(now all these urls should be called with there namespace "myapp:urlname")
Create a middleware.py file in your app with this:
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import ImproperlyConfigured
from django.urls import resolve
class LoginRequiredAccess:
"""All urls starting with the given prefix require the user to be logged in"""
APP_NAME = 'myapp'
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured(
"Requires the django's authentication middleware"
" to be installed.")
user = request.user
if resolve(request.path).app_name == self.APP_NAME: # match app_name defined in myapp.urls.py
if not user.is_authenticated:
path = request.get_full_path()
return redirect_to_login(path)
return self.get_response(request)
Put "myapp.middleware.LoginRequiredAccess" in your MIDDLEWARE constant from settings.py
Then in your main project urls.py
urlpatterns = [
path('foobar', include('otherapp.urls')), # this will not be redirected
path('whatever', include('myapp.urls')), # all these urls will be redirected to login
]
On of the avantage of this method is it can still works with a root url path, e.g path('', include('myapp.urls')), while the others will do an infinite redirect loop.
I'm wondering if there is any solution to make it works like this:
/app/app.py
class AppConfig(AppConfig):
login_required = True
/project/urls.py
urlpatterns = [
url(r'app/', include('app.urls', namespace='app'))
]
/common/middleare.py
def LogMiddleware(get_response):
def middleware(request):
# solution 1
app = get_app(request)
if app.login_required is True and request.is_authenticated is Fasle:
return HttpResponseRedirect(redirect_url)
# solution 2
url_space = get_url_space(request.get_raw_uri())
if url_space.namespace in ['app', 'admin', 'staff', 'manage'] and \
request.is_authenticated is False:
return HttpResponseRedirect(redirect_url)
I will check if there is any methoded to get the app or url name of a request. I think it looks prettier.