200 lines
7.3 KiB
Python
200 lines
7.3 KiB
Python
|
import hashlib
|
||
|
|
||
|
from django.db import models
|
||
|
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
|
||
|
from django.utils.encoding import python_2_unicode_compatible
|
||
|
from django.utils.http import urlquote
|
||
|
from django.core.mail import send_mail
|
||
|
from django.dispatch import receiver
|
||
|
from django.utils import timezone
|
||
|
from django.utils.translation import ugettext_lazy as _
|
||
|
try:
|
||
|
from django.utils.encoding import force_text
|
||
|
except ImportError:
|
||
|
from django.utils.encoding import force_unicode as force_text
|
||
|
from allauth.account.signals import user_signed_up
|
||
|
|
||
|
|
||
|
class MyUserManager(UserManager):
|
||
|
"""
|
||
|
Custom User Model manager.
|
||
|
|
||
|
It overrides default User Model manager's create_user() and create_superuser,
|
||
|
which requires username field.
|
||
|
"""
|
||
|
|
||
|
def create_user(self, email, password=None, **kwargs):
|
||
|
user = self.model(email=email, **kwargs)
|
||
|
user.set_password(password)
|
||
|
user.save()
|
||
|
return user
|
||
|
|
||
|
def create_superuser(self, email, password, **kwargs):
|
||
|
user = self.model(email=email, is_staff=True, is_superuser=True, **kwargs)
|
||
|
user.set_password(password)
|
||
|
user.save()
|
||
|
return user
|
||
|
|
||
|
|
||
|
class DemoUser(AbstractBaseUser, PermissionsMixin):
|
||
|
"""A site-specific user model.
|
||
|
|
||
|
Important: You don't have to use a custom user model. I did it here because
|
||
|
I didn't want a username to be part of the system and I wanted other data
|
||
|
to be part of the user and not in a separate table.
|
||
|
|
||
|
You can avoid the username issue without writing a custom model but it
|
||
|
becomes increasingly obtuse as time goes on. Write a custom user model, then
|
||
|
add a custom admin form and model.
|
||
|
|
||
|
Remember to change ``AUTH_USER_MODEL`` in ``settings.py``.
|
||
|
"""
|
||
|
|
||
|
email = models.EmailField(_('email address'), blank=False, unique=True)
|
||
|
first_name = models.CharField(_('first name'), max_length=40, blank=True, null=True, unique=False)
|
||
|
last_name = models.CharField(_('last name'), max_length=40, blank=True, null=True, unique=False)
|
||
|
display_name = models.CharField(_('display name'), max_length=14, blank=True, null=True, unique=False)
|
||
|
is_staff = models.BooleanField(_('staff status'), default=False,
|
||
|
help_text=_('Designates whether the user can log into this admin '
|
||
|
'site.'))
|
||
|
is_active = models.BooleanField(_('active'), default=True,
|
||
|
help_text=_('Designates whether this user should be treated as '
|
||
|
'active. Unselect this instead of deleting accounts.'))
|
||
|
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
|
||
|
|
||
|
objects = MyUserManager()
|
||
|
|
||
|
USERNAME_FIELD = 'email'
|
||
|
REQUIRED_FIELDS = []
|
||
|
|
||
|
class Meta:
|
||
|
verbose_name = _('user')
|
||
|
verbose_name_plural = _('users')
|
||
|
db_table = 'auth_user'
|
||
|
abstract = False
|
||
|
|
||
|
def get_absolute_url(self):
|
||
|
# TODO: what is this for?
|
||
|
return "/users/%s/" % urlquote(self.email) # TODO: email ok for this? better to have uuid?
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
if self.first_name:
|
||
|
return self.first_name
|
||
|
elif self.display_name:
|
||
|
return self.display_name
|
||
|
return 'You'
|
||
|
|
||
|
def get_full_name(self):
|
||
|
"""
|
||
|
Returns the first_name plus the last_name, with a space in between.
|
||
|
"""
|
||
|
full_name = '%s %s' % (self.first_name, self.last_name)
|
||
|
return full_name.strip()
|
||
|
|
||
|
def get_short_name(self):
|
||
|
"Returns the short name for the user."
|
||
|
return self.first_name
|
||
|
|
||
|
def guess_display_name(self):
|
||
|
"""Set a display name, if one isn't already set."""
|
||
|
if self.display_name:
|
||
|
return
|
||
|
|
||
|
if self.first_name and self.last_name:
|
||
|
dn = "%s %s" % (self.first_name, self.last_name[0]) # like "Andrew E"
|
||
|
elif self.first_name:
|
||
|
dn = self.first_name
|
||
|
else:
|
||
|
dn = 'You'
|
||
|
self.display_name = dn.strip()
|
||
|
|
||
|
def email_user(self, subject, message, from_email=None):
|
||
|
"""
|
||
|
Sends an email to this User.
|
||
|
"""
|
||
|
send_mail(subject, message, from_email, [self.email])
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.email
|
||
|
|
||
|
def natural_key(self):
|
||
|
return (self.email,)
|
||
|
|
||
|
|
||
|
@python_2_unicode_compatible
|
||
|
class UserProfile(models.Model):
|
||
|
"""Profile data about a user.
|
||
|
Certain data makes sense to be in the User model itself, but some
|
||
|
is more "profile" data than "user" data. I think this is things like
|
||
|
date-of-birth, favourite colour, etc. If you have domain-specific
|
||
|
profile information you might create additional profile classes, like
|
||
|
say UserGeologistProfile.
|
||
|
"""
|
||
|
user = models.OneToOneField(DemoUser, on_delete=models.CASCADE, primary_key=True, verbose_name='user', related_name='profile')
|
||
|
|
||
|
# I oscillate between whether the ``avatar_url`` should be
|
||
|
# a) in the User model
|
||
|
# b) in this UserProfile model
|
||
|
# c) in a table of it's own to track multiple pictures, with the
|
||
|
# "current" avatar as a foreign key in User or UserProfile.
|
||
|
avatar_url = models.CharField(max_length=256, blank=True, null=True)
|
||
|
|
||
|
dob=models.DateField(verbose_name="dob", blank=True, null=True)
|
||
|
|
||
|
def __str__(self):
|
||
|
return force_text(self.user.email)
|
||
|
|
||
|
class Meta():
|
||
|
db_table = 'user_profile'
|
||
|
|
||
|
|
||
|
@receiver(user_signed_up)
|
||
|
def set_initial_user_names(request, user, sociallogin=None, **kwargs):
|
||
|
"""
|
||
|
When a social account is created successfully and this signal is received,
|
||
|
django-allauth passes in the sociallogin param, giving access to metadata on the remote account, e.g.:
|
||
|
|
||
|
sociallogin.account.provider # e.g. 'twitter'
|
||
|
sociallogin.account.get_avatar_url()
|
||
|
sociallogin.account.get_profile_url()
|
||
|
sociallogin.account.extra_data['screen_name']
|
||
|
|
||
|
See the socialaccount_socialaccount table for more in the 'extra_data' field.
|
||
|
|
||
|
From http://birdhouse.org/blog/2013/12/03/django-allauth-retrieve-firstlast-names-from-fb-twitter-google/comment-page-1/
|
||
|
"""
|
||
|
|
||
|
preferred_avatar_size_pixels=256
|
||
|
|
||
|
picture_url = "http://www.gravatar.com/avatar/{0}?s={1}".format(
|
||
|
hashlib.md5(user.email.encode('UTF-8')).hexdigest(),
|
||
|
preferred_avatar_size_pixels
|
||
|
)
|
||
|
|
||
|
if sociallogin:
|
||
|
# Extract first / last names from social nets and store on User record
|
||
|
if sociallogin.account.provider == 'twitter':
|
||
|
name = sociallogin.account.extra_data['name']
|
||
|
user.first_name = name.split()[0]
|
||
|
user.last_name = name.split()[1]
|
||
|
|
||
|
if sociallogin.account.provider == 'facebook':
|
||
|
user.first_name = sociallogin.account.extra_data['first_name']
|
||
|
user.last_name = sociallogin.account.extra_data['last_name']
|
||
|
#verified = sociallogin.account.extra_data['verified']
|
||
|
picture_url = "http://graph.facebook.com/{0}/picture?width={1}&height={1}".format(
|
||
|
sociallogin.account.uid, preferred_avatar_size_pixels)
|
||
|
|
||
|
if sociallogin.account.provider == 'google':
|
||
|
user.first_name = sociallogin.account.extra_data['given_name']
|
||
|
user.last_name = sociallogin.account.extra_data['family_name']
|
||
|
#verified = sociallogin.account.extra_data['verified_email']
|
||
|
picture_url = sociallogin.account.extra_data['picture']
|
||
|
|
||
|
profile = UserProfile(user=user, avatar_url=picture_url)
|
||
|
profile.save()
|
||
|
|
||
|
user.guess_display_name()
|
||
|
user.save()
|