[manual] Add User fields to the UserProfile model.

And keep the fields updated, by copying on UserProfile creation and
updating the UserProfile object whenever we're updating the User
object, and add management commands to (1) initially ensure that they
match and (2) check that they still match (aka that the updating code
is working).

The copy_user_to_userprofile migration needs to be run after this is
deployed to prod.

(imported from commit 0a598d2e10b1a7a2f5c67dd5140ea4bb8e1ec0b8)
This commit is contained in:
Tim Abbott
2013-03-08 13:53:00 -05:00
parent 659ccc2174
commit b82edb6fd6
5 changed files with 89 additions and 7 deletions

View File

@@ -75,6 +75,10 @@ def user_sessions(user):
return [s for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id] return [s for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id]
def do_deactivate(user_profile): def do_deactivate(user_profile):
user_profile.is_active = False;
user_profile.set_unusable_password()
user_profile.save(update_fields=["is_active", "password"])
user_profile.user.set_unusable_password() user_profile.user.set_unusable_password()
user_profile.user.is_active = False user_profile.user.is_active = False
user_profile.user.save(update_fields=["is_active", "password"]) user_profile.user.save(update_fields=["is_active", "password"])
@@ -98,6 +102,10 @@ def do_deactivate(user_profile):
def do_change_user_email(user_profile, new_email): def do_change_user_email(user_profile, new_email):
old_email = user_profile.user.email old_email = user_profile.user.email
user_profile.email = new_email
user_profile.save(update_fields=["email"])
user_profile.user.email = new_email user_profile.user.email = new_email
user_profile.user.save(update_fields=["email"]) user_profile.user.save(update_fields=["email"])
@@ -469,6 +477,12 @@ def log_subscription_property_change(user_email, property, property_dict):
def do_activate_user(user_profile, log=True, join_date=timezone.now()): def do_activate_user(user_profile, log=True, join_date=timezone.now()):
user = user_profile.user user = user_profile.user
user_profile.is_active = True
user_profile.set_password(initial_password(user_profile.email))
user_profile.date_joined = join_date
user_profile.save(update_fields=["is_active", "date_joined", "password"])
user.is_active = True user.is_active = True
user.set_password(initial_password(user.email)) user.set_password(initial_password(user.email))
user.date_joined = join_date user.date_joined = join_date
@@ -486,10 +500,13 @@ def do_change_password(user_profile, password, log=True, commit=True,
if hashed_password: if hashed_password:
# This is a hashed password, not the password itself. # This is a hashed password, not the password itself.
user.password = password user.password = password
user_profile.set_password(password)
else: else:
user.set_password(password) user.set_password(password)
user_profile.set_password(password)
if commit: if commit:
user.save(update_fields=["password"]) user.save(update_fields=["password"])
user_profile.save(update_fields=["password"])
if log: if log:
log_event({'type': 'user_change_password', log_event({'type': 'user_change_password',
'user': user.email, 'user': user.email,

View File

@@ -42,8 +42,14 @@ def bulk_create_users(realms, users_raw):
# Now create user_profiles # Now create user_profiles
profiles_to_create = [] profiles_to_create = []
for (email, full_name, short_name, active) in users: for (email, full_name, short_name, active) in users:
user = users_by_email[email]
domain = email.split('@')[1] domain = email.split('@')[1]
profile = UserProfile(user=users_by_email[email], pointer=-1, profile = UserProfile(user=user, pointer=-1,
is_active=user.is_active,
is_staff=user.is_staff,
date_joined=user.date_joined,
email=user.email,
password=user.password,
realm=realms[domain], realm=realms[domain],
full_name=full_name, short_name=short_name) full_name=full_name, short_name=short_name)
profile.api_key = initial_api_key(email) profile.api_key = initial_api_key(email)

View File

@@ -0,0 +1,18 @@
from optparse import make_option
from django.core.management.base import BaseCommand
from zephyr.models import UserProfile, User
class Command(BaseCommand):
option_list = BaseCommand.option_list
help = "Copy all the shared fields from User to UserProfile."
def handle(self, *args, **options):
for user_profile in UserProfile.objects.all():
user = user_profile.user
user_profile.email = user.email
user_profile.is_active = user.is_active
user_profile.is_staff = user.is_staff
user_profile.date_joined = user.date_joined
user_profile.password = user.password
user_profile.save(update_fields=["email", "is_active", "is_staff", "date_joined", "password"])

View File

@@ -0,0 +1,19 @@
from optparse import make_option
from django.core.management.base import BaseCommand
from zephyr.models import UserProfile, User
class Command(BaseCommand):
option_list = BaseCommand.option_list
help = "Diff all the shared fields between User to UserProfile."
def handle(self, *args, **options):
for user_profile in UserProfile.objects.all():
user = user_profile.user
def diff_fields(user_version, user_profile_version):
if user_version != user_profile_version:
print "Some values differ for %s!" % (user_profile.user.email,)
diff_fields(user.email, user_profile.email)
diff_fields(user.is_active, user_profile.is_active)
diff_fields(user.is_staff, user_profile.is_staff)
diff_fields(user.date_joined, user_profile.date_joined)

View File

@@ -1,6 +1,6 @@
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User, AbstractBaseUser, UserManager
from zephyr.lib.cache import cache_with_key, update_user_profile_cache, \ from zephyr.lib.cache import cache_with_key, update_user_profile_cache, \
update_user_cache, user_profile_by_id_cache_key, \ update_user_cache, user_profile_by_id_cache_key, \
user_profile_by_email_cache_key user_profile_by_email_cache_key
@@ -52,8 +52,21 @@ class Realm(models.Model):
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
class UserProfile(models.Model): class UserProfile(AbstractBaseUser):
# Fields from models.AbstractUser minus last_name and first_name, which we don't use
email = models.EmailField(blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
# User Legacy code: the object that goes with this UserProfile.
# Plan is to for a short time maintain them both in sync, then
# later we'll dump the old User field, and perhaps later than
# that, rename the surviving field to just User.
user = models.OneToOneField(User) user = models.OneToOneField(User)
# Our custom site-specific fields
full_name = models.CharField(max_length=100) full_name = models.CharField(max_length=100)
short_name = models.CharField(max_length=100) short_name = models.CharField(max_length=100)
pointer = models.IntegerField() pointer = models.IntegerField()
@@ -63,16 +76,20 @@ class UserProfile(models.Model):
enable_desktop_notifications = models.BooleanField(default=True) enable_desktop_notifications = models.BooleanField(default=True)
enter_sends = models.NullBooleanField(default=False) enter_sends = models.NullBooleanField(default=False)
def __repr__(self): objects = UserManager()
return (u"<UserProfile: %s %s>" % (self.user.email, self.realm)).encode("utf-8")
def __str__(self):
return self.__repr__()
@classmethod @classmethod
def create(cls, user, realm, full_name, short_name): def create(cls, user, realm, full_name, short_name):
"""When creating a new user, make a profile for him or her.""" """When creating a new user, make a profile for him or her."""
if not cls.objects.filter(user=user): if not cls.objects.filter(user=user):
profile = cls(user=user, pointer=-1, realm=realm, profile = cls(user=user, pointer=-1, realm=realm,
# User Legacy code:
is_active=user.is_active,
is_staff=user.is_staff,
date_joined=user.date_joined,
email=user.email,
password=user.password,
# end User Legacy code
full_name=full_name, short_name=short_name) full_name=full_name, short_name=short_name)
profile.api_key = initial_api_key(user.email) profile.api_key = initial_api_key(user.email)
profile.save() profile.save()
@@ -81,6 +98,11 @@ class UserProfile(models.Model):
Subscription.objects.create(user_profile=profile, recipient=recipient) Subscription.objects.create(user_profile=profile, recipient=recipient)
return profile return profile
def __repr__(self):
return (u"<UserProfile: %s %s>" % (self.user.email, self.realm)).encode("utf-8")
def __str__(self):
return self.__repr__()
# Make sure we flush the UserProfile object from our memcached # Make sure we flush the UserProfile object from our memcached
# whenever we save it. # whenever we save it.
post_save.connect(update_user_profile_cache, sender=UserProfile) post_save.connect(update_user_profile_cache, sender=UserProfile)