django i18n_patterns hide default lang_code from url - django

I'm using the i18n_patterns to add a prefix of current lang_code to my url.
urlpatterns += i18n_patterns('',
url(r'^', include('blaszczakphoto2.gallery.urls')),
)
It allowes me to get urls like /en/about-us/ , /pl/about-us/ etc..
My default language is pl
LANGUAGE_CODE = 'pl'
I want url like /about-us/ for clients viewing my site in polish lenguage. Is there any way to hide lang_code prefix from url for default lang_code?

Django >=1.10 can handle this natively. There is a new prefix_default_language argument in i18n_patterns function.
Setting prefix_default_language to False removes the prefix from
the default language (LANGUAGE_CODE). This can be useful when adding
translations to existing site so that the current URLs won’t change.
Source: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#language-prefix-in-url-patterns
Example:
# Main urls.py:
urlpatterns = i18n_patterns(
url(r'^', include('my_app.urls', namespace='my_app')),
prefix_default_language=False
)
# my_app.urls.py:
url(r'^contact-us/$', ...),
# settings:
LANGUAGE_CODE = 'en' # Default language without prefix
LANGUAGES = (
('en', _('English')),
('cs', _('Czech')),
)
The response of example.com/contact-us/ will be in English and example.com/cs/contact-us/ in Czech.

Here is a very simple package: django-solid-i18n-urls
After setup, urls without language prefix will always use default language, that is specified in settings.LANGUAGE_CODE. Redirects will not occur.
If url will have language prefix, then this language will be used.
Also answered here: https://stackoverflow.com/a/16580467/821594.

I faced this problem and solved this way:
Created an alternative i18n_patterns that do not prefix the site main language (defined in settings.LANGUAGE_CODE).
Created an alternative middleware that only uses the URL prefixes language to activate the current language.
I didn't see any side-effect using this technique.
The code:
# coding: utf-8
"""
Cauê Thenório - cauelt(at)gmail.com
This snippet makes Django do not create URL languages prefix (i.e. /en/)
for the default language (settings.LANGUAGE_CODE).
It also provides a middleware that activates the language based only on the URL.
This middleware ignores user session data, cookie and 'Accept-Language' HTTP header.
Your urls will be like:
In your default language (english in example):
/contact
/news
/articles
In another languages (portuguese in example):
/pt/contato
/pt/noticias
/pt/artigos
To use it, use the 'simple_i18n_patterns' instead the 'i18n_patterns'
in your urls.py:
from this_sinppet import simple_i18n_patterns as i18n_patterns
And use the 'SimpleLocaleMiddleware' instead the Django's 'LocaleMiddleware'
in your settings.py:
MIDDLEWARE_CLASSES = (
...
'this_snippet.SimpleLocaleMiddleware'
...
)
Works on Django >=1.4
"""
import re
from django.conf import settings
from django.conf.urls import patterns
from django.core.urlresolvers import LocaleRegexURLResolver
from django.middleware.locale import LocaleMiddleware
from django.utils.translation import get_language, get_language_from_path
from django.utils import translation
class SimpleLocaleMiddleware(LocaleMiddleware):
def process_request(self, request):
if self.is_language_prefix_patterns_used():
lang_code = (get_language_from_path(request.path_info) or
settings.LANGUAGE_CODE)
translation.activate(lang_code)
request.LANGUAGE_CODE = translation.get_language()
class NoPrefixLocaleRegexURLResolver(LocaleRegexURLResolver):
#property
def regex(self):
language_code = get_language()
if language_code not in self._regex_dict:
regex_compiled = (re.compile('', re.UNICODE)
if language_code == settings.LANGUAGE_CODE
else re.compile('^%s/' % language_code, re.UNICODE))
self._regex_dict[language_code] = regex_compiled
return self._regex_dict[language_code]
def simple_i18n_patterns(prefix, *args):
"""
Adds the language code prefix to every URL pattern within this
function, when the language not is the main language.
This may only be used in the root URLconf, not in an included URLconf.
"""
pattern_list = patterns(prefix, *args)
if not settings.USE_I18N:
return pattern_list
return [NoPrefixLocaleRegexURLResolver(pattern_list)]
The code above is available on:
https://gist.github.com/cauethenorio/4948177

You might want to check yawd-translations which does exactly that. If you don't care about the extra functionality (manage languages from db), you can have a look at the urls.translation_patterns method and the middleware.TranslationMiddleware to see how it can be done.

This is my solution:
Create django middleware: django_app/lib/middleware/locale.py
from django.utils import translation
class SwitchLanguageMiddleware(object):
def process_request(self, request):
lang = request.GET.get('lang', '')
if lang:
translation.activate(lang)
request.LANGUAGE_CODE = translation.get_language()
def process_response(self, request, response):
request.session['django_language'] = translation.get_language()
if 'Content-Language' not in response:
response['Content-Language'] = translation.get_language()
translation.deactivate()
return response
It's read the get parameter of request and if it has lang attribute, then switching language. Ex.: /about-us/?lang=pl
Include this middleware to settings.py:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.common.CommonMiddleware',
'django_app.libs.middleware.locale.SwitchLanguageMiddleware',
)

Related

Django Internationalization and localization instead of /en use a specific domain for each language

i have 3 domains, which will be one website, just that each one will serve a language
ej: (right now i am in dev mode so i only have localhost to test)
drenglish.com for 'en'
drspanish.com for 'es_ES'
drportugueses for 'pt_BR'
automatically Internationalization and localization works with /lang added to the urls, but i dont want that
i tried transurlvania but cant figure it out, is there an easy way to use my translated po file and tie it to a domain ?
site framework seem to maybe be the answer, but i get lost when they tell to create different settings and urls files for each domain so you can add the right SITE_ID
i am starting to use i18n for the internationalization and localization
settings;
middleware: 'django.contrib.sites.middleware.CurrentSiteMiddleware'
USE_I18N = True
#drspanish.com = ID 5
#drportugueses = ID 4
#drenglish.com = ID 1
SITE_ID = 1
from django.utils.translation import ugettext_lazy as _ LANGUAGES = (
('en', _('English')),
('pt_BR', _('Portuguese')),
('es_ES', _('Spanish')), )
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'), )
urls(main);
from django.conf.urls.i18n import i18n_patterns
urlpatterns = [
#urls for english + global
path('admin/', admin.site.urls),
path('users/', include('users.urls')), #for allauth
path('accounts/', include('allauth.urls')), #for allauth
url(r'^ckeditor/', include('ckeditor_uploader.urls')), ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # comment out this line from the +... if the website is live, its only to serve image in debug mode
urlpatterns += i18n_patterns(
url('', include('home.urls')), #for allauth
url('contact', include('contact.urls')),
url(r'^videos/', include('video.urls')),
url(r'^dash/', include('UDash.urls')),
url(r'^appointment/', include('appointment.urls')),
url(r'^blog/', include('blog.urls')),
url(r'^cart/', include('cart.urls')),
url(r'^shop/', include('shop.urls')), )
You can use a middleware to set your language depending on the domain you are accessing. For example, create a middleware SetLanguageToDomain which will look like this,
from django.utils import translation
from django.contrib.sites.shortcuts import get_current_site
class SetLanguageToDomain:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
current_site = get_current_site(request).domain
if current_site == 'drenglish':
user_language = 'en'
elif current_site == 'drspanish':
user_language = 'es_ES'
else:
user_language = 'pt_BR'
translation.activate(user_language)
request.session[translation.LANGUAGE_SESSION_KEY] = user_language
response = self.get_response(request)
return response
Then add this to your middlewares after the locale middleware.
Hope this helps!

Django disable trailing slash

I'm using Django 2.0.6 (using the development server for now) and am having issues with automatic trailing slashes being appended to URL's.
My goal is to have a URL structure as follows:
/teams/ => Index view for all teams
/teams/create => Form view to create a new team (note the lack of trailing slash)
/teams/xyz/ => Index view for team with slug xyz
/teams/xyz/delete => Form view to delete team xyz (require confirmation, etc)
The problem is that, somewhere in Django framework, a trailing slash is automatically being appended to 'create' URL's. Because of this, the router the attempts to load the team index page for a team with slug 'create'. Obviously, a workaround would be to stop using slugs and use ID's, but that seems like an unnecessary concession.
Looking around, it seems that setting APPEND_SLASH to False should tell CommonMiddleware to stop appending slashes, but this didn't help.
Is there a way to easily accomplish my URL scheme and, if not, what's the idiomatic Django way to do this?
urls.py:
from django.urls import path
urlpatterns = [
path('create', views.create, name='create'),
path('<slug:team_slug>/', views.view, name='view'),
path('<slug:team_slug>/invite/', views.invite, name='invite')
]
settings.py:
APPEND_SLASH = False
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'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',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
Links are correctly generated with the following:
<a class="btn btn-primary" href="{% url 'teams:create' %}">New Team</a>
Which translates to: /teams/create
However, when the user clicks the link, it appears to be immediately redirected to /teams/create/
Create a directory named common in your project directory.
Create __init__.py and redirect.py in common directory.
# redirect.py
from django.conf import settings
from django.core import urlresolvers
from django import http
'''
Based on django/middleware/common.py
Django convention is to add trailing slashes to most urls
This method does the opposite and redirects trailing slashes to the
no trailing slash url if it exists
'''
class RedirectTrailingSlashMiddleware(object):
def process_request(self, request):
if settings.APPEND_SLASH:
return
if '/admin' in request.path:
settings.APPEND_SLASH = True
return
new_url = old_url = request.path
if (old_url.endswith('/')):
urlconf = getattr(request, 'urlconf', None)
if (not urlresolvers.is_valid_path(request.path_info, urlconf) and
urlresolvers.is_valid_path(request.path_info[:-1], urlconf)):
new_url = new_url[:-1]
if settings.DEBUG and request.method == 'POST':
raise RuntimeError((""
"You called this URL via POST, but the URL ends "
"in a slash and you have APPEND_SLASH set. Django can't "
"redirect to the non-slash URL while maintaining POST data. "
"Change your form to point to %s (note no trailing "
"slash), or set APPEND_SLASH=True in your Django "
"settings.") % (new_url))
if new_url == old_url:
# No redirects required.
return
return http.HttpResponsePermanentRedirect(new_url)
In your settings.py, add:
MIDDLEWARE = [
'common.redirect.RedirectTrailingSlashMiddleware',
# Add as first element
]
Source/credit: http://bunwich.blogspot.com/2013/06/django-change-trailing-slash-url.html

django links to the comment function in the backend does not work

After transfer the website to another server with higher python version stoped working the links in the backend to the comment function. Does somebody know where could the problem be? Thank you.
I´m using django 1.6 (I hope I could update it soon)
This is code in the settings.py on the root level (only the important parts)
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.syndication',
'django_comments',
'blog',
'hyper',
'webmanager',
'watson',
)
INSTALLED_APPS += ('django_summernote', )
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'webmanager.middleware.MyPageMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'urls'
SITE_ID='1'
#WSGI_APPLICATION = 'marxelhino.wsgi.application'
# fluent-comments settings:
FLUENT_COMMENTS_USE_EMAIL_MODERATION = True
FLUENT_COMMENTS_MODERATE_AFTER_DAYS = 14
FLUENT_COMMENTS_CLOSE_AFTER_DAYS = 60
FLUENT_COMMENTS_AKISMET_ACTION = 'moderate'
AKISMET_API_KEY = None # Add your Akismet key here to enable Akismet support
AKISMET_IS_TEST = True # for development/example apps.
This is the code I have in the urls.py on the root level
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from marxelhino.feeds import Latest
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'marxelinho.views.home', name='home'),
url(r'^$', 'blog.views.index', name='index'),
url(r'^blog/(?P<slug>[a-zA-Z0-9_.-]+)/$$', 'blog.views.detail', name='detail'),
url(r'^blog/by_year/(?P<year>\d{4})/$', 'blog.views.post_year', name='list-years'),
url(r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/$', 'blog.views.post_month', name='list-month'),
url(r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', 'blog.views.post_day', name='list-day'),
url(r'^category/(?P<slug>[a-zA-Z0-9_./-]+)/$$', 'blog.views.category_detail', name='category_detail'),
#url(r'^blog/comments/', include('fluent_comments.urls')),
url(r'^comments/', include('django_comments.urls')),
(r'^latest/feed/$', Latest()),
url(r'^tinymce/', include('tinymce.urls')),
url(r'^search/', include('search.urls')),
url(r'^summernote/', include('django_summernote.urls')),
url(r'^accounts/', include('registration.backends.default.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.STATIC_ROOT}),
(r'^media/(?P<path>.*)$', 'django.views.static.serve',
# (r'^suche/', include('haystack.urls')),
{'document_root': settings.MEDIA_ROOT}),
)
the code in urls.py in the folder django_comments
from django.conf.urls import patterns, url
urlpatterns = patterns('django_comments.views',
url(r'^post/$', 'comments.post_comment', name='comments-post-comment'),
url(r'^posted/$', 'comments.comment_done', name='comments-comment-done'),
url(r'^flag/(\d+)/$', 'moderation.flag', name='comments-flag'),
url(r'^flagged/$', 'moderation.flag_done', name='comments-flag-done'),
url(r'^delete/(\d+)/$', 'moderation.delete', name='comments-delete'),
url(r'^deleted/$', 'moderation.delete_done', name='comments-delete-done'),
url(r'^approve/(\d+)/$', 'moderation.approve', name='comments-approve'),
url(r'^approved/$', 'moderation.approve_done', name='comments-approve-done'),
)
urlpatterns += patterns('',
url(r'^cr/(\d+)/(.+)/$', 'django.contrib.contenttypes.views.shortcut', name='comments-url-redirect'),
)
and this code is in the folder fluent_comments and in the file views.py
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.http import HttpResponse, HttpResponseBadRequest
from django.template.loader import render_to_string
from django.template import RequestContext
from django.contrib import comments
from django.contrib.comments import signals
from django.contrib.comments.views.comments import CommentPostBadRequest
from django.utils.html import escape
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST
from fluent_comments import appsettings
import json
#csrf_protect
#require_POST
def post_comment_ajax(request, using=None):
"""
Post a comment, via an Ajax call.
"""
if not request.is_ajax():
return HttpResponseBadRequest("Expecting Ajax call")
# This is copied from django.contrib.comments.
# Basically that view does too much, and doesn't offer a hook to change the rendering.
# The request object is not passed to next_redirect for example.
#
# This is a separate view to integrate both features. Previously this used django-ajaxcomments
# which is unfortunately not thread-safe (it it changes the comment view per request).
# Fill out some initial data fields from an authenticated user, if present
data = request.POST.copy()
if request.user.is_authenticated():
if not data.get('name', ''):
data["name"] = request.user.get_full_name() or request.user.username
if not data.get('email', ''):
data["email"] = request.user.email
# Look up the object we're trying to comment about
ctype = data.get("content_type")
object_pk = data.get("object_pk")
if ctype is None or object_pk is None:
return CommentPostBadRequest("Missing content_type or object_pk field.")
try:
object_pk = long(object_pk)
model = models.get_model(*ctype.split(".", 1))
target = model._default_manager.using(using).get(pk=object_pk)
except ValueError:
return CommentPostBadRequest("Invalid object_pk value: {0}".format(escape(object_pk)))
except TypeError:
return CommentPostBadRequest("Invalid content_type value: {0}".format(escape(ctype)))
except AttributeError:
return CommentPostBadRequest("The given content-type {0} does not resolve to a valid model.".format(escape(ctype)))
except ObjectDoesNotExist:
return CommentPostBadRequest("No object matching content-type {0} and object PK {1} exists.".format(escape(ctype), escape(object_pk)))
except (ValueError, ValidationError) as e:
return CommentPostBadRequest("Attempting go get content-type {0!r} and object PK {1!r} exists raised {2}".format(escape(ctype), escape(object_pk), e.__class__.__name__))
# Do we want to preview the comment?
preview = "preview" in data
# Construct the comment form
form = comments.get_form()(target, data=data)
# Check security information
if form.security_errors():
return CommentPostBadRequest("The comment form failed security verification: {0}".format)
# If there are errors or if we requested a preview show the comment
if preview:
comment = form.get_comment_object() if not form.errors else None
return _ajax_result(request, form, "preview", comment, object_id=object_pk)
if form.errors:
return _ajax_result(request, form, "post", object_id=object_pk)
# Otherwise create the comment
comment = form.get_comment_object()
comment.ip_address = request.META.get("REMOTE_ADDR", None)
if request.user.is_authenticated():
comment.user = request.user
# Signal that the comment is about to be saved
responses = signals.comment_will_be_posted.send(
sender = comment.__class__,
comment = comment,
request = request
)
for (receiver, response) in responses:
if response is False:
return CommentPostBadRequest("comment_will_be_posted receiver {0} killed the comment".format(receiver.__name__))
# Save the comment and signal that it was saved
comment.save()
signals.comment_was_posted.send(
sender = comment.__class__,
comment = comment,
request = request
)
return _ajax_result(request, form, "post", comment, object_id=object_pk)
def _ajax_result(request, form, action, comment=None, object_id=None):
# Based on django-ajaxcomments, BSD licensed.
# Copyright (c) 2009 Brandon Konkle and individual contributors.
#
# This code was extracted out of django-ajaxcomments because
# django-ajaxcomments is not threadsafe, and it was refactored afterwards.
success = True
json_errors = {}
if form.errors:
for field_name in form.errors:
field = form[field_name]
json_errors[field_name] = _render_errors(field)
success = False
json_return = {
'success': success,
'action': action,
'errors': json_errors,
'object_id': object_id,
'use_threadedcomments': bool(appsettings.USE_THREADEDCOMMENTS),
}
if comment is not None:
context = {
'comment': comment,
'action': action,
'preview': (action == 'preview'),
'USE_THREADEDCOMMENTS': appsettings.USE_THREADEDCOMMENTS,
}
comment_html = render_to_string('comments/comment.html', context, context_instance=RequestContext(request))
json_return.update({
'html': comment_html,
'comment_id': comment.id,
'parent_id': None,
'is_moderated': not comment.is_public, # is_public flags changes in comment_will_be_posted
})
if appsettings.USE_THREADEDCOMMENTS:
json_return['parent_id'] = comment.parent_id
json_response = json.dumps(json_return)
return HttpResponse(json_response, content_type="application/json")
def _render_errors(field):
"""
Render form errors in crispy-forms style.
"""
template = '{0}/layout/field_errors.html'.format(appsettings.CRISPY_TEMPLATE_PACK)
return render_to_string(template, {
'field': field,
'form_show_errors': True,
})
and this code in the folder fluent_comments and in the file urls.py
try:
# Django 1.6 requires this
from django.conf.urls import url, patterns, include
except ImportError:
# Django 1.3 compatibility, kept in minor release
from django.conf.urls.defaults import url, patterns, include
urlpatterns = patterns('fluent_comments.views',
url(r'^post/ajax/$', 'post_comment_ajax', name='comments-post-comment-ajax'),
url(r'', include('django.contrib.comments.urls')),
)

django url variable to get parameters

I've a part of my urls.py file as follow:
urlpatterns = patterns('',
url(r'^$', DetailHomePublic),
url(r'^demo/$', TemplateView.as_view(template_name='public/demo.html')),
url(r'^privacy/$', TemplateView.as_view(template_name='public/privacy.html')),
url(r'^terms/$', TemplateView.as_view(template_name='public/terms.html')),
url(r'^login/$', Login),
url(r'^logout/$', Logout),
url(r'^lost_password/$', LostPassword),
url(r'^register/$', Register),
)
I've done a middleware which catch if a parameters named language is passed. It's used to change the language of the website.
class SetLanguageMiddleware:
"""
Middleware which changes the language if the GET variable named language has been set with a valid language referenced on the settings.py file
"""
def process_request(self, request):
from django.utils import translation
from django.conf import settings
from django.utils.translation import LANGUAGE_SESSION_KEY
if 'language' in request.GET:
language = request.GET['language']
#check if the language is defined in the settings.LANGUAGES global settings
if any(language in code for code in settings.LANGUAGES):
#set the given language in the session and active it for the current page too
request.session[LANGUAGE_SESSION_KEY] = language
translation.activate(request.session[LANGUAGE_SESSION_KEY])
Now, for only this given page, I'd like to add a language prefix to provide a multilingual website.
Is it possible to catach the prefix on the urls file and add it to the GET parameters, to have something like that:
urlpatterns = patterns('',
url(r'^(?P<language>[a-z]{3})$', DetailHomePublic),
url(r'^(?P<language>[a-z]{3})demo/$', TemplateView.as_view(template_name='public/demo.html')),
)
...
Thank you

Django: How can I apply the login_required decorator to my entire site (excluding static media)?

The example provides a snippet for an application level view, but what if I have lots of different (and some non-application) entries in my "urls.py" file, including templates? How can I apply this login_required decorator to each of them?
(r'^foo/(?P<slug>[-\w]+)/$', 'bugs.views.bug_detail'),
(r'^$', 'django.views.generic.simple.direct_to_template', {'template':'homepage.html'}),
Dropped this into a middleware.py file in my project root (taken from http://onecreativeblog.com/post/59051248/django-login-required-middleware)
from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile
EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
class LoginRequiredMiddleware:
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
you can copy from your urls.py).
Requires authentication middleware and template context processors to be
loaded. You'll get an error if they aren't.
"""
def process_request(self, request):
assert hasattr(request, 'user'), "The Login Required middleware\
requires authentication middleware to be installed. Edit your\
MIDDLEWARE_CLASSES setting to insert\
'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
'django.core.context_processors.auth'."
if not request.user.is_authenticated():
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
Then appended projectname.middleware.LoginRequiredMiddleware to my MIDDLEWARE_CLASSES in settings.py.
For those who have come by later to this, you might find that django-stronghold fits your usecase well. You whitelist any urls you want to be public, the rest are login required.
https://github.com/mgrouchy/django-stronghold
Here's a slightly shorter middleware.
from django.contrib.auth.decorators import login_required
class LoginRequiredMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
if not getattr(view_func, 'login_required', True):
return None
return login_required(view_func)(request, *view_args, **view_kwargs)
You'll have to set "login_required" to False on each view you don't need to be logged in to see:
Function-views:
def someview(request, *args, **kwargs):
# body of view
someview.login_required = False
Class-based views:
class SomeView(View):
login_required = False
# body of view
#or
class SomeView(View):
# body of view
someview = SomeView.as_view()
someview.login_required = False
This means you'll have to do something about the login-views, but I always end up writing my own auth-backend anyway.
Some of the previous answers are either outdated (older version of Django), or introduce poor programming practices (hardcoding URLs, not using routes). Here's my take that is more DRY and sustainable/maintainable (adapted from Mehmet's answer above).
To highlight the improvements here, this relies on giving URLs route names (which are much more reliable than using hard-coded URLs/URIs that change and have trailing/leading slashes).
from django.utils.deprecation import MiddlewareMixin
from django.urls import resolve, reverse
from django.http import HttpResponseRedirect
from my_project import settings
class LoginRequiredMiddleware(MiddlewareMixin):
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings by setting a tuple of routes to ignore
"""
def process_request(self, request):
assert hasattr(request, 'user'), """
The Login Required middleware needs to be after AuthenticationMiddleware.
Also make sure to include the template context_processor:
'django.contrib.auth.context_processors.auth'."""
if not request.user.is_authenticated:
current_route_name = resolve(request.path_info).url_name
if not current_route_name in settings.AUTH_EXEMPT_ROUTES:
return HttpResponseRedirect(reverse(settings.AUTH_LOGIN_ROUTE))
And in the settings.py file, you can define the following:
AUTH_EXEMPT_ROUTES = ('register', 'login', 'forgot-password')
AUTH_LOGIN_ROUTE = 'register'
Here is the classical LoginRequiredMiddleware for Django 1.10+:
from django.utils.deprecation import MiddlewareMixin
class LoginRequiredMiddleware(MiddlewareMixin):
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
you can copy from your urls.py).
"""
def process_request(self, request):
assert hasattr(request, 'user'), """
The Login Required middleware needs to be after AuthenticationMiddleware.
Also make sure to include the template context_processor:
'django.contrib.auth.context_processors.auth'."""
if not request.user.is_authenticated:
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
Noteworthy differences:
path.to.LoginRequiredMiddleware should be included in MIDDLEWARE not MIDDLEWARE_CLASSES in settings.py.
is_authenticated is a bool not a method.
see the docs for more info (although some parts are not very clear).
Use middleware.
http://www.djangobook.com/en/2.0/chapter17/
and
http://docs.djangoproject.com/en/1.2/topics/http/middleware/#topics-http-middleware
I'm assuming this didn't change a whole lot in 1.2
Middleware allows you to create a class with methods who will process every request at various times/conditions, as you define.
for example process_request(request) would fire before your view, and you can force authentication and authorization at this point.
In addition to meder omuraliev answer if you want exempt url like this (with regexp):
url(r'^my/url/(?P<pk>[0-9]+)/$', views.my_view, name='my_url')
add it to EXEMPT_URLS list like this:
LOGIN_EXEMPT_URLS = [r'^my/url/([0-9]+)/$']
r'..' in the beginning of the string necessary.
Django Login Required Middleware
Put this code in middleware.py :
from django.http import HttpResponseRedirect
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from re import compile
EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
class LoginRequiredMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'user')
if not request.user.is_authenticated:
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
And, in settings.py :
LOGIN_URL = '/app_name/login'
LOGIN_EXEMPT_URLS=(
r'/app_name/login/',
)
MIDDLEWARE_CLASSES = (
# ...
'python.path.to.LoginRequiredMiddleware',
)
Like this :
'app_name.middleware.LoginRequiredMiddleware'
If you have lots of views and you do not want to touch any one you can just use Middleware for this issue. Try code below:
import traceback
from django.contrib.auth.decorators import login_required
class RejectAnonymousUsersMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
current_route_name = resolve(request.path_info).url_name
if current_route_name in settings.AUTH_EXEMPT_ROUTES:
return
if request.user.is_authenticated:
return
return login_required(view_func)(request, *view_args, **view_kwargs)
Cautions:
You must add this middleware to the bottommost of middleware section
of settings.py
You should put this variable in settings.py
AUTH_EXEMPT_ROUTES = ('register', 'login', 'forgot-password')
Thanks from #Ehsan Ahmadi
In the newer version of Django, the middleware should be written like this( my Djang version = 4.1.1) :
middleware.py
from django.contrib.auth.decorators import login_required
from django.urls import resolve
from django.utils.deprecation import MiddlewareMixin
AUTH_EXEMPT_ROUTES = ('captcha-image', 'login', 'captcha')
class RejectAnonymousUsersMiddleware(MiddlewareMixin):
def process_view(self, request, view_func, view_args, view_kwargs):
current_route_name = resolve(request.path_info).url_name
if request.user.is_authenticated:
return
if current_route_name in AUTH_EXEMPT_ROUTES:
return
return login_required(view_func)(request, *view_args, **view_kwargs)
settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'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',
'myproject.middleware.RejectAnonymousUsersMiddleware',
]
Here's an example for new-style middleware in Django 1.10+:
from django.contrib.auth.decorators import login_required
from django.urls import reverse
def login_required_middleware(get_response):
"""
Require user to be logged in for all views.
"""
exceptions = {'/admin/login/'}
def middleware(request):
if request.path in exceptions:
return get_response(request)
return login_required(get_response, login_url=reverse('admin:login'))(request)
return middleware
This example exempts the admin login form to avoid redirect loop, and uses that form as the login url.