diff --git a/django/contrib/auth/apps.py b/django/contrib/auth/apps.py index 83790007d9..18f149659b 100644 --- a/django/contrib/auth/apps.py +++ b/django/contrib/auth/apps.py @@ -3,8 +3,10 @@ from django.core import checks from django.db.models.signals import post_migrate from django.utils.translation import gettext_lazy as _ +from . import get_user_model from .checks import check_models_permissions, check_user_model from .management import create_permissions +from .signals import user_logged_in class AuthConfig(AppConfig): @@ -16,5 +18,8 @@ class AuthConfig(AppConfig): create_permissions, dispatch_uid="django.contrib.auth.management.create_permissions" ) + if hasattr(get_user_model(), 'last_login'): + from .models import update_last_login + user_logged_in.connect(update_last_login, dispatch_uid='update_last_login') checks.register(check_user_model, checks.Tags.models) checks.register(check_models_permissions, checks.Tags.models) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index dad7d288c5..d931277c66 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,6 +1,5 @@ from django.contrib import auth from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager -from django.contrib.auth.signals import user_logged_in from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.mail import send_mail @@ -21,9 +20,6 @@ def update_last_login(sender, user, **kwargs): user.save(update_fields=['last_login']) -user_logged_in.connect(update_last_login) - - class PermissionManager(models.Manager): use_in_migrations = True diff --git a/tests/auth_tests/models/__init__.py b/tests/auth_tests/models/__init__.py index 8b8cb69ef3..af58cb7118 100644 --- a/tests/auth_tests/models/__init__.py +++ b/tests/auth_tests/models/__init__.py @@ -4,6 +4,7 @@ from .custom_user import ( ) from .invalid_models import CustomUserNonUniqueUsername from .is_active import IsActiveTestUser1 +from .minimal import MinimalUser from .uuid_pk import UUIDUser from .with_foreign_key import CustomUserWithFK, Email from .with_integer_username import IntegerUsernameUser @@ -11,5 +12,6 @@ from .with_integer_username import IntegerUsernameUser __all__ = ( 'CustomUser', 'CustomUserWithoutIsActiveField', 'CustomPermissionsUser', 'CustomUserWithFK', 'Email', 'ExtensionUser', 'IsActiveTestUser1', - 'UUIDUser', 'CustomUserNonUniqueUsername', 'IntegerUsernameUser' + 'MinimalUser', 'UUIDUser', 'CustomUserNonUniqueUsername', + 'IntegerUsernameUser', ) diff --git a/tests/auth_tests/models/minimal.py b/tests/auth_tests/models/minimal.py new file mode 100644 index 0000000000..7b8931b231 --- /dev/null +++ b/tests/auth_tests/models/minimal.py @@ -0,0 +1,6 @@ +from django.db import models + + +class MinimalUser(models.Model): + REQUIRED_FIELDS = () + USERNAME_FIELD = 'id' diff --git a/tests/auth_tests/test_signals.py b/tests/auth_tests/test_signals.py index 8fb2046e1c..93a8689463 100644 --- a/tests/auth_tests/test_signals.py +++ b/tests/auth_tests/test_signals.py @@ -1,8 +1,12 @@ +from django.apps import apps from django.contrib.auth import authenticate, signals from django.contrib.auth.models import User +from django.core.exceptions import FieldDoesNotExist from django.test import TestCase, override_settings from django.test.client import RequestFactory +from .models import MinimalUser + @override_settings(ROOT_URLCONF='auth_tests.urls') class SignalTestCase(TestCase): @@ -82,3 +86,23 @@ class SignalTestCase(TestCase): def test_failed_login_without_request(self): authenticate(username='testclient', password='bad') self.assertIsNone(self.login_failed[0]['request']) + + def test_login_with_custom_user_without_last_login_field(self): + """ + The user_logged_in signal is only registered if the user model has a + last_login field. + """ + last_login_receivers = signals.user_logged_in.receivers + try: + signals.user_logged_in.receivers = [] + with self.assertRaises(FieldDoesNotExist): + MinimalUser._meta.get_field('last_login') + with self.settings(AUTH_USER_MODEL='auth_tests.MinimalUser'): + apps.get_app_config('auth').ready() + self.assertEqual(signals.user_logged_in.receivers, []) + + with self.settings(AUTH_USER_MODEL='auth.User'): + apps.get_app_config('auth').ready() + self.assertEqual(len(signals.user_logged_in.receivers), 1) + finally: + signals.user_logged_in.receivers = last_login_receivers