Safely importing models in django's middleware - django

I have been trying to import a model in a middleware process function but I am getting the error:
Value: Model class x doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS
I am guessing the problem is that the application setup is not complete, however, all my attempts to solve this have hit a wall.
middleware.py function
from notifications.models import Notification
...
class CheckNotificationMiddleware(object):
"""
Checks clicked notification to mark it as read
"""
def process_request(self, request):
if request.user.is_authenticated():
notification_id = request.GET.get('notification_id')
if notification_id:
AppConfig.get_model('Notification')
try:
notification = Notification.objects.get(
pk=notification_id, status=0, recipient=request.user)
notification.status = 1
notification.save()
except ObjectDoesNotExist:
pass
settings.py
INSTALLED_APPS = (
...
'notifications',
...
)
I have tried a couple of things including...
from django.apps import AppConfig
AppConfig.get_model('Notification')
I am using Django version 1.11 and python 2.7

you have to include notifications under the
INSTALLED_APPS = [
'notifications',
]
in the settings file

Related

How to save extra fields on registration using custom user model in DRF + django-rest-auth

Using Django REST Framework (DRF), with django-rest-auth, I created a custom user model with one extra field. My aim is to use the django-rest-auth registration endpoint to register a new user in one request, and thus sending all the data to create a new user, including the data for the extra field.
I am using AbstractUser, since it seems recommended for beginners, where more advanced developers could use AbstractBaseUser. This is also why the following SO answers looks too complicated for what I want to achieve: link here.
I know this question has been asked multiple times, but the answers are not exactly what I am looking for. For a beginner like me this is complicated stuff.
So, my question is, can anyone explain how to achieve what I want?
I am using:
Django 2.1.4
django-allauth 0.38.0
django-rest-auth 0.9.3
djangorestframework 3.9.0
Here's the code that I have up until this point:
Used this tutorial to get to this code
settings.py:
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '!gxred^*penrx*qlb=#p)p(vb!&6t78z4n!poz=zj+a0_9#sw1'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'django.contrib.sites',
'allauth',
'allauth.account',
'rest_auth.registration',
'users',
]
SITE_ID = 1
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',
]
ROOT_URLCONF = 'DRF_custom_user.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'DRF_custom_user.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
AUTH_USER_MODEL = 'users.CustomUser'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
users.models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
preferred_locale = models.CharField(blank=True, null=True, max_length=2)
users.admin.py:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'preferred_locale']
admin.site.register(CustomUser, CustomUserAdmin)
users.forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = CustomUser
fields = ('email', )
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = UserChangeForm.Meta.fields
I went looking for an answer myself. Spend some time digging in the source code. I realize this solution may be missing the actual validation for the extra fields added to the custom user model, but I will look into that later.
What I have written below I wrote with a potential blog post in mind.
I am going to assume you know how to set up a DRF project and install the above packages. The django-rest-auth documentation is clear on how to install that package (https://django-rest-auth.readthedocs.io/en/latest/index.html), make sure to also follow the steps to install the part of django-rest-auth for user registration.
Create a new app ‘users’
This app will hold my custom code for implementing the custom user model. I also install it in the Django main settings file:
settings.py:
INSTALLED_APPS = [
...
'users',
]
Create my custom user model
Notice that I just added one custom field, but you can add whatever fields you want ofcourse.
users.models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
preferred_locale = models.CharField(max_length=2, blank=True, null=True)
Tell django to use the CustomUser model
settings.py:
…
AUTH_USER_MODEL = 'users.CustomUser'
Register Custom user model at Django admin
users.admin.py:
from django.contrib import admin
from .models import CustomUser
admin.site.register(CustomUser)
Make migrations and run them
This is the first time I do this for this project.
In command line:
python manage.py makemigrations users
python manage.py migrate
Registering new users with extra fields
If you start the Django development server now, you’ll see in the admin that you can see the custom user model, with the extra fields.
But when you go to ‘http://127.0.0.1:8000/rest-auth/registration/’ you don’t see the extra fields yet.
In the process of user registration two important classes are used, namely:
a serializer ‘rest_auth.registration.RegisterSerializer’
an adapter ‘allauth.account.adapter.DefaultAccountAdapter’
We’ll make a custom version of both of these, inheriting all the functionality of it’s parent class.
Creating a custom RegisterSerializer
Create a new file ‘serializers.py’ in the users app/folder.
users.serializers.py:
from rest_framework import serializers
from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email
from rest_auth.registration.serializers import RegisterSerializer
class CustomRegisterSerializer(RegisterSerializer):
preferred_locale = serializers.CharField(
required=False,
max_length=2,
)
def get_cleaned_data(self):
data_dict = super().get_cleaned_data()
data_dict['preferred_locale'] = self.validated_data.get('preferred_locale', '')
return data_dict
Here I create a new field for each extra field on the custom user model. So in my case a added this:
preferred_locale = serializers.CharField(
required=False,
max_length=2,
)
Also, the get_cleaned_data method should return a dict which contains all the data for the fields that you want to have saved when registering a new user.
This is what the original method (of the default RegisterSerializer looks like):
def get_cleaned_data(self):
return {
'username': self.validated_data.get('username', ''),
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', '')
}
As you can see it returns a dictionary, containing all the data for the new user. You want to add a keyval entry to this dictionary for each extra field you have added to your custom user model.
In my case, needing only to add data for the field ‘preferred_locale’, this is the resulting method:
def get_cleaned_data(self):
data_dict = super().get_cleaned_data()
data_dict['preferred_locale'] = self.validated_data.get('preferred_locale', '')
return data_dict
Tell django to use this new serializer
settings.py:
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'users.serializers.CustomRegisterSerializer',
}
Preventing errors
If you will try to register a new user, you might get the following error in the console where your development server is running:
ConnectionRefusedError: [Errno 111] Connection refused
Although a user is still created, you can fix this error by adding the following line to your settings.py file:
settings.py:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Another error that will occur when you delete a user is:
django.db.utils.OperationalError: no such table: allauth_socialaccount
To solve this, add this to your settings.py:
settings.py:
INSTALLED_APPS = [
...
'allauth.socialaccount',
]
After that, you should apply migrations before you can continue:
python manage.py migrate
Creating a custom AccountAdapter
After the above steps, going to ‘http://127.0.0.1:8000/rest-auth/registration/’ will show you the extra fields. But when you register a new user, and send the data for the extra fields, the extra field’s data is not saved.
The last thing we need to do to solve this is to create a custom AccountAdapter
In our users app/folder create a new file named ‘adapter.py’:
users.adapter.py:
from allauth.account.adapter import DefaultAccountAdapter
class CustomAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
user = super().save_user(request, user, form, commit)
data = form.cleaned_data
user.preferred_locale = data.get('preferred_locale')
user.save()
return user
Here, if you have followed the above steps correctly, you can access the data of the extra added fields in the form.cleaned_data dictionary. This is the dictionary that is returned by the get_cleaned_data method from our custom RegisterSerializer.
In the save_user method above, we can then use this data and save it to the appropriate fields, like so:
user.preferred_locale = data.get('preferred_locale')
Tell Django to use this new adapters
settings.py:
ACCOUNT_ADAPTER = 'users.adapter.CustomAccountAdapter'
Now you can register your user, using the django-rest-auth registration endpoint '/rest-auth/registration/', and send the data for the extra fields you added. This will all be saved in one request.
Again, I realize that custom validation for each field needs to be added. But that's another topic that I will dive into later, and update the post when I found out how that works precisely.
Let's break your question. Please note that I am explaining the basics of Django REST Framework to you.
Overriding User's Model
[x] Step 1: You override User model: You did this. Alternative? Yes. Create a model that has OneToOneForeignKey pointed to User model.
[x] Step 2: Use this CustomUserModel. To do this, you need to set AUTH_USER_MODEL in settings.py Link to official documentation You did this.
[ ] Step 3: Create a UserManager to handle user's registration and other information. You haven't done this.
Registration from API
[ ] Create a serializer that clearly mentions all the required fields that you're expecting from an end user. You can even use serializer.ModelSerializer if there are no custom fields.
[ ] Handle explicit verifications in serializer itself. Use def validate(self, attrs) if required. Here is the official document link.
[ ] Finally, create a view and use APIView as you will want to register a user using UserManager that you created above.
I can also refer to you an app that I built myself. Here's the link: DRF-USER. I customized User model to some extent and followed the same process.
Hope this helps.

view in one app is not recognized by different view - Django

I have a Django project that I am building. Within the django project I have two apps. one is users and one is accounts. I want to call a view from the acounts app called user_synapse in the users app. I have imported the accounts views python file to import all the methods. I am then calling the method but I am getting an error that says the view method that I am trying to call is not defined. It was working earlier but now it is not working at all. I added the django app name to the settings file and the main urls file. Here is the code I have:
Users app:
views.py file:
#import all references from other apps
from accounts.views import *
from groups.models import *
from groups.views import *
...
# create synapse user
user_synapse(request)
accounts app:
views.py file:
#import all references from other apps
from groups.models import *
from groups.views import *
from users.views import *
from users.models import *
# synapse import statements that are needed
from synapse_pay_rest import Client, Node, Transaction
from synapse_pay_rest import User as SynapseUser
from synapse_pay_rest.models.nodes import AchUsNode
...
# ensure someone is logged in
#login_required
# create a synapse user
def user_synapse(request):
# grab the logged in user
user = request.user
# grab users profile
profile = Profile.objects.get(user = user)
# set user items before creating items
legal_name = profile.first_name + " " + profile.last_name
note = legal_name + " has just created his synapse profile "
supp_id = generate_number()
cip_tag = user.id
# grab the account type
account = profile.business
if account == 'INDIVIDUAL':
account = False
if account == 'BUSINESS':
account = True
# the following is all of the information that is required in order to make a
# new user within the Synapse application
args = {
'email':str(user.email),
'phone_number':str(profile.phone),
'legal_name':str(legal_name),
'note': str(note),
'supp_id':str(supp_id),
'is_business':account,
'cip_tag':cip_tag,
}
# the following is the request to the synapse api as well as the returned
# json that contains information that needs to ba saved in local database
create_user = SynapseUser.create(client, **args)
response = create_user.json
# the following updates the current profile to add the users synapse id within
# the local database.
if response:
synapse_id = response['_id']
updateProfile = profile
updateProfile.synapse_id = synapse_id
updateProfile.save()
the view does exist. why in the world is it saying that the method is not defined:
NameError at /personal/
name 'user_synapse' is not defined
settings.py file:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'general',
'users',
'localflavor',
'groups',
'accounts',
]
It was working earlier but now it is not working and I have no idea why.
The problem is that you have a circular import: in users.views.py
from accounts.views import *
in accounts.views.py
from users.views import *
This is not permitted in Python. See import dependency in Python for more info.

enable a model on Django Admin on Dreamhost

Hi I've created a new app (Paginas) but I cannot see inside the admin page. I've added 'paginas' to INSTALLED_APP. My admin.py is
# coding=utf-8
from django.contrib import admin
from .models import Pagina
class PaginaAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("titulo",)}
list_display = ('titulo' , 'contenido')
class Media:
js = ('/layout/grappelli/tinymce/jscripts/tiny_mce/tiny_mce.js','/layout/grappelli/tinymce_setup/tinymce_setup.js')
admin.site.register(Pagina,PaginaAdmin)
I am using passenger_wsgi.py file, I've touched tmp/restart.txt file, I've killed python process
I don't know what else can I do
The project you can see it on github https://github.com/totechess/paralisis_cerebral
What kind of error do you get when you go to your site yoursite.com/admin/
Did you add admin to INSTALLED_APPS? Note the S
....
'django.contrib.admin',
....
Perhaps your urls.py are not updated properly? What is the error?

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.

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.