2
0
Эх сурвалжийг харах

Fixed #7220 -- Allowed AbstractBaseUser.last_login to be null.

Thanks veena for the suggestion and Simon Charette and Kévin Etienne for reviews.
Tim Graham 10 жил өмнө
parent
commit
a2479f46f3

+ 19 - 0
django/contrib/auth/migrations/0005_alter_user_last_login_null.py

@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('auth', '0004_alter_user_username_opts'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='user',
+            name='last_login',
+            field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
+        ),
+    ]

+ 2 - 2
django/contrib/auth/models.py

@@ -172,7 +172,7 @@ class UserManager(BaseUserManager):
         email = self.normalize_email(email)
         user = self.model(username=username, email=email,
                           is_staff=is_staff, is_active=True,
-                          is_superuser=is_superuser, last_login=now,
+                          is_superuser=is_superuser,
                           date_joined=now, **extra_fields)
         user.set_password(password)
         user.save(using=self._db)
@@ -190,7 +190,7 @@ class UserManager(BaseUserManager):
 @python_2_unicode_compatible
 class AbstractBaseUser(models.Model):
     password = models.CharField(_('password'), max_length=128)
-    last_login = models.DateTimeField(_('last login'), default=timezone.now)
+    last_login = models.DateTimeField(_('last login'), blank=True, null=True)
 
     is_active = True
 

+ 7 - 0
django/contrib/auth/tests/test_models.py

@@ -156,6 +156,13 @@ class AbstractUserTestCase(TestCase):
         self.assertEqual(message.from_email, "from@domain.com")
         self.assertEqual(message.to, [abstract_user.email])
 
+    def test_last_login_default(self):
+        user1 = User.objects.create(username='user1')
+        self.assertIsNone(user1.last_login)
+
+        user2 = User.objects.create_user(username='user2')
+        self.assertIsNone(user2.last_login)
+
 
 class IsActiveTestCase(TestCase):
     """

+ 1 - 1
django/contrib/auth/tokens.py

@@ -56,7 +56,7 @@ class PasswordResetTokenGenerator(object):
         key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
 
         # Ensure results are consistent across DB backends
-        login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
+        login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)
 
         value = (six.text_type(user.pk) + user.password +
                 six.text_type(login_timestamp) + six.text_type(timestamp))

+ 6 - 2
docs/ref/contrib/auth.txt

@@ -81,8 +81,12 @@ Fields
 
     .. attribute:: last_login
 
-        A datetime of the user's last login. Is set to the current date/time by
-        default.
+        A datetime of the user's last login.
+
+        .. versionchanged:: 1.8
+
+            This field will be ``null`` if the user has never logged in.
+            Previously it was set to the current date/time by default.
 
     .. attribute:: date_joined
 

+ 20 - 0
docs/releases/1.8.txt

@@ -407,6 +407,26 @@ officially supports.
 This also includes dropping support for PostGIS 1.3 and 1.4 as these versions
 are not supported on versions of PostgreSQL later than 8.4.
 
+``AbstractUser.last_login`` allows null values
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :attr:`AbstractUser.last_login <django.contrib.auth.models.User.last_login>`
+field now allows null values. Previously, it defaulted to the time when the user
+was created which was misleading if the user never logged in. Please run the
+database migration. If your custom user inherits from ``AbstractUser`` and you
+wish to set ``last_login`` to ``NULL`` for users who haven't logged in, you can
+run this query::
+
+    from django.db import models
+    from django.contrib.auth import get_user_model
+    from django.contrib.auth.models import AbstractBaseUser
+
+    UserModel = get_user_model()
+    if issubclass(UserModel, AbstractBaseUser):
+        UserModel._default_manager.filter(
+            last_login=models.F('date_joined')
+        ).update(last_login=None)
+
 Miscellaneous
 ~~~~~~~~~~~~~