Fixed #7220 -- Allowed AbstractBaseUser.last_login to be null.
Thanks veena for the suggestion and Simon Charette and Kévin Etienne for reviews.
This commit is contained in:
parent
1a31d9ef91
commit
a2479f46f3
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -172,7 +172,7 @@ class UserManager(BaseUserManager):
|
||||||
email = self.normalize_email(email)
|
email = self.normalize_email(email)
|
||||||
user = self.model(username=username, email=email,
|
user = self.model(username=username, email=email,
|
||||||
is_staff=is_staff, is_active=True,
|
is_staff=is_staff, is_active=True,
|
||||||
is_superuser=is_superuser, last_login=now,
|
is_superuser=is_superuser,
|
||||||
date_joined=now, **extra_fields)
|
date_joined=now, **extra_fields)
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save(using=self._db)
|
user.save(using=self._db)
|
||||||
|
@ -190,7 +190,7 @@ class UserManager(BaseUserManager):
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class AbstractBaseUser(models.Model):
|
class AbstractBaseUser(models.Model):
|
||||||
password = models.CharField(_('password'), max_length=128)
|
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
|
is_active = True
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,13 @@ class AbstractUserTestCase(TestCase):
|
||||||
self.assertEqual(message.from_email, "from@domain.com")
|
self.assertEqual(message.from_email, "from@domain.com")
|
||||||
self.assertEqual(message.to, [abstract_user.email])
|
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):
|
class IsActiveTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -56,7 +56,7 @@ class PasswordResetTokenGenerator(object):
|
||||||
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
|
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
|
||||||
|
|
||||||
# Ensure results are consistent across DB backends
|
# 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 +
|
value = (six.text_type(user.pk) + user.password +
|
||||||
six.text_type(login_timestamp) + six.text_type(timestamp))
|
six.text_type(login_timestamp) + six.text_type(timestamp))
|
||||||
|
|
|
@ -81,8 +81,12 @@ Fields
|
||||||
|
|
||||||
.. attribute:: last_login
|
.. attribute:: last_login
|
||||||
|
|
||||||
A datetime of the user's last login. Is set to the current date/time by
|
A datetime of the user's last login.
|
||||||
default.
|
|
||||||
|
.. 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
|
.. attribute:: date_joined
|
||||||
|
|
||||||
|
|
|
@ -407,6 +407,26 @@ officially supports.
|
||||||
This also includes dropping support for PostGIS 1.3 and 1.4 as these versions
|
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.
|
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
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue