Source code for superdjango.contrib.accounts.profiles.models

# Imports

from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
import hashlib

# Exports

__all__ = (
    "ProfileMixin",
    "ProfileModel",
    "ProfileSection",
    "ProfileSectionItem",
)

# Constants

AUTH_USER_MODEL = settings.AUTH_USER_MODEL

LOGIN_REDIRECT_URL = getattr(settings, "LOGIN_REDIRECT_URL")

STATIC_URL = getattr(settings, "STATIC_URL")

# Descriptors


[docs]class ProfileSection(object): """Collects related profile fields together for display."""
[docs] def __init__(self, title): self.items = list() self.title = title
[docs] def add_item(self, label, value): """Add an item to the section. :param label: The label for the item, i.e. field's verbose name. :type label: str :param value: The value of the item. :rtype: ProfileSectionItem """ item = ProfileSectionItem(label, value) self.items.append(item) return item
[docs]class ProfileSectionItem(object): """An individual field within a section."""
[docs] def __init__(self, label, value): self.label = label self.value = value
# Mixins
[docs]class ProfileMixin(object): """Establishes the API for user profiles.""" # class Meta: # abstract = True def __str__(self): return self.get_full_name() @property def date_joined(self): # noinspection PyUnresolvedReferences return self.user.date_joined @property def email(self): """Aliased for the user's email address.""" # noinspection PyUnresolvedReferences return self.user.email
[docs] @classmethod def for_user(cls, user, create=False, profile_name="profile"): """Get (or create) the profile for a given user. :param user: The user. :type user: AUTH_USER_MODEL :param create: Indicates whether a profile should be created if one does not already exist. :type create: bool :param profile_name: The related name of the profile field. :type profile_name: str :rtype: instance :returns: An instance of a profile model for the user. """ # There's no use in trying if the user is anonymous. if not user.is_authenticated: return None # It is possible that the profile is already available on the user object. Try that before executing the rest of # the code. But does this actually save a query? # noinspection PyUnresolvedReferences try: return getattr(user, profile_name) except (AttributeError, cls.DoesNotExist): pass # noinspection PyUnresolvedReferences try: # noinspection PyUnresolvedReferences return cls.objects.get(user=user) except cls.DoesNotExist: pass if create: # noinspection PyUnresolvedReferences profile, created = cls.objects.get_or_create(user=user) return profile return None
@property def full_name(self): """Aliased for ``get_full_name()``.""" return self.get_full_name() @property def full_name_with_honorifics(self): """Alias of ``get_full_name_with_honorifics()``.""" return self.get_full_name_with_honorifics()
[docs] def get_form_class(self, request=None): """Get the form class to use for editing the profile. :param request: The current request instance. :type request: HttpRequest """ raise NotImplementedError()
[docs] def get_full_name(self): """Get the full name of the user, or the user name if a full name is not available. :rtype: str """ raise NotImplementedError()
[docs] def get_full_name_with_honorifics(self): """Get the full name of the user, including prefix and suffix, if available. :rtype: str """ raise NotImplementedError()
# noinspection PyMethodMayBeStatic
[docs] def get_redirect_url(self): """Get the URL to which the user prefers to be redirected after logging in. :rtype: str .. note:: By default, the ``settings.LOGIN_REDIRECT_URL`` is returned. The profile model may implement a user preference for the redirect. """ return LOGIN_REDIRECT_URL
[docs] def get_thumbnail(self): """Get the thumbnail URL for the user's avatar. :rtype: str """ return self._get_gravatar_url()
[docs] def get_sections(self): """Get the sections to display for profile detail. :rtype: list[ProfileSection] """ raise NotImplementedError()
# noinspection PyMethodMayBeStatic
[docs] def get_time_zone(self): """Get the timezone of the user. If unavailable, the default time zone should be returned. :rtype: str .. note:: The profile model that uses this mixin should implement a field (or other means) of establishing the user's time zone (using geo-location for example), defaulting to ``settings.TIME_ZONE`` only if a time zone preference is unavailable. """ return settings.TIME_ZONE
@property def has_thumbnail(self): """Indicates the profile has a thumbnail image. :rtype bool """ return True @property def last_login(self): """Alias for the user's last login.""" # noinspection PyUnresolvedReferences return self.user.last_login @property def redirect_url(self): """Alias for ``get_redirect_url()``.""" return self.get_redirect_url() @property def sections(self): """Alias for ``get_sections()``.""" return self.get_sections() @property def thumbnail(self): """Aliased for ``get_thumbnail()``.""" return self.get_thumbnail() @property def username(self): """Aliased for the user's ``username``.""" # noinspection PyUnresolvedReferences return self.user.username def _get_gravatar_url(self): """Get the user's gravatar.com URL. :rtype: str """ encoded_email = self.email.strip().lower().encode("utf-8") email_hash = hashlib.md5(encoded_email).hexdigest() # IDEA: The gavatar could be cached locally to save a URL call. return "https://secure.gravatar.com/avatar/%s.jpg" % email_hash
[docs]class ProfileModel(ProfileMixin, models.Model): """An implementation of the profile mixin with sensible defaults. .. note:: No fields with foreign keys are included. This model extends the mixin to establishe some common profile attributes: .. code-block:: python from superdjango.contrib.accounts.profiles.models import ProfileModel class Profile(ProfileModel): user = models.OneToOneField( AUTH_USER_MODEL, help_text=_("The user account to which this profile is attached."), related_name="profile", verbose_name=_("user") ) class Meta: verbose_name = _("User Profile") verbose_name_plural = _("User Profiles") """ address = models.TextField( _("address"), blank=True, help_text=_("Enter your mailing address."), null=True ) first_name = models.CharField( _("first name"), blank=True, help_text=_("Your given name."), max_length=128 ) gravatar_enabled = models.BooleanField( _("enable gravatar"), default=True, help_text=_("Use gravatar.com for your profile picture.") ) last_name = models.CharField( _("last name"), blank=True, help_text=_("Your family name."), max_length=128 ) middle_name = models.CharField( _("middle name"), blank=True, help_text=_("Your middle name or initial."), max_length=128, null=True ) phone_number = models.CharField( _("phone number"), blank=True, help_text=_("Enter your phone number."), max_length=128 ) photo = models.FileField( _("photo"), blank=True, help_text=_("Upload a photo to use for your profile picture."), null=True ) prefix = models.CharField( _("prefix"), blank=True, help_text=_("Enter a prefix to appear before your name or leave blank for nothing."), max_length=64 ) suffix = models.CharField( _("suffix"), blank=True, help_text=_("Enter a suffix to appear after your name or leave blank for nothing."), max_length=64 ) # redirect_to choices must be established by the project. redirect_to = models.CharField( _("redirect to"), blank=True, help_text=_("By default, go to this page after logging in."), max_length=256, ) # time_zone choices must be established by the project. time_zone = models.CharField( _("time zone"), blank=True, help_text=_("Select the timezone in which you reside or work."), max_length=128 ) class Meta: abstract = True
[docs] def get_form_class(self, request=None): from .forms import ProfileForm return ProfileForm
[docs] def get_full_name(self): a = list() a.append(self.first_name) if self.middle_name: a.append(self.middle_name) a.append(self.last_name) if len(a) == 0: a.append(self.username) return " ".join(a)
[docs] def get_full_name_with_honorifics(self): a = list() if self.prefix: a.append(self.prefix) a.append(self.get_full_name()) if self.suffix: a.append(self.suffix) return " ".join(a)
[docs] def get_redirect_url(self): """Get the user's redirect choice or the ``LOGIN_REDIRECT_URL`` if no choice has been made. :rtype: str """ return self.redirect_to or LOGIN_REDIRECT_URL
[docs] def get_sections(self): """Get profile sections.""" a = list() contact = ProfileSection(_("Contact")) contact.add_item(_("Name"), self.get_full_name_with_honorifics()) contact.add_item(_("Email"), self.email) contact.add_item(_("Phone"), self.phone_number) contact.add_item(_("Address"), self.address) a.append(contact) user = ProfileSection(_("User")) user.add_item(_("User Name"), self.username) user.add_item(_("Since"), self.date_joined) a.append(user) prefs = ProfileSection(_("Preferences")) prefs.add_item(_("Redirect To"), self.get_redirect_url()) prefs.add_item(_("Gravatar Enabled"), _("Yes") if self.gravatar_enabled else _("No")) prefs.add_item(_("Time Zone"), self.get_time_zone()) a.append(prefs) return a
[docs] def get_thumbnail(self): """Uses gravatar or an uploaded photo.""" if self.gravatar_enabled: return self._get_gravatar_url() return self.photo.url
[docs] def get_time_zone(self): """Get the time zone of the user or the default timezone. :rtype: str """ return self.time_zone or settings.TIME_ZONE
@property def has_thumbnail(self): """Determines a gravatar or photo is available.""" if self.gravatar_enabled: return True if self.photo: return True return False @property def title(self): """Automated object title for UI views.""" return self.full_name