Source code for superdjango.contrib.accounts.profiles.forms
# Imports
from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext_lazy as _
from superdjango.conf import SUPERDJANGO
from superdjango.html.library import Fieldset
from superdjango.contrib.i18n.timezones import COMMON_TIMEZONE_CHOICES
from ..utils import get_profile_model, get_user_profile
ProfileModel = get_profile_model()
UserModel = get_user_model()
# Exports
__all__ = (
"ProfileForm",
)
# Constants
DEFAULT_REDIRECT_TO_CHOICES = (
("/", _("Home")),
("/accounts/profile/", _("My Account")),
)
LOGIN_REDIRECT_URL = getattr(settings, "LOGIN_REDIRECT_URL", "/")
# Forms
[docs]class ProfileForm(forms.ModelForm):
"""A base form for user profiles based on the ``ProfileModel``."""
field_order = [
"prefix",
"first_name",
"last_name",
"suffix",
"address",
"phone_number",
"email",
"username",
"redirect_to",
"time_zone",
"gravatar_enabled",
"photo",
]
address = forms.CharField(
label=_("Address"),
help_text=_("Enter your mailing address."),
widget=forms.Textarea,
required=False
)
email = forms.EmailField(
label=_("Email Address"),
help_text=_("Enter your email address."),
required=False
)
first_name = forms.CharField(
label=_("First Name"),
help_text=_("Your given name."),
max_length=128,
required=True
)
gravatar_enabled = forms.BooleanField(
help_text=_("Indicates whether to use gravatar.com for your profile photos."),
label=_("Enable Gravatar")
)
last_name = forms.CharField(
label=_("Last Name"),
help_text=_("Your family name."),
max_length=128,
required=True
)
middle_name = forms.CharField(
label=_("Middle Name"),
help_text=_("Your middle name or initial."),
max_length=128,
required=False
)
phone_number = forms.CharField(
label=_("Phone Number"),
help_text=_("Enter your phone number."),
required=False
)
photo = forms.FileField(
label=_("Photo"),
help_text=_("Upload a photo to use for your profile picture."),
required=False
)
prefix = forms.CharField(
label=_("Prefix"),
help_text=_("Enter a prefix to appear before your name or leave blank for nothing."),
max_length=64,
required=False
)
# Default redirect choices will be overridden by account settings.
redirect_to = forms.ChoiceField(
choices=SUPERDJANGO.REDIRECT_TO_CHOICES or DEFAULT_REDIRECT_TO_CHOICES,
initial=LOGIN_REDIRECT_URL,
help_text=_("By default, go to this page after logging in."),
label=_("Redirect To"),
required=True
)
suffix = forms.CharField(
label=_("Suffix"),
help_text=_("Enter a suffix to appear after your name or leave blank for nothing."),
max_length=64,
required=False
)
# Default choices will be overridden by account settings.
time_zone = forms.ChoiceField(
choices=SUPERDJANGO.TIMEZONE_CHOICES or COMMON_TIMEZONE_CHOICES,
label=_("Timezone"),
help_text=_("Select the timezone in which you reside or work."),
required=False
)
username = forms.CharField(
label=_("User Name"),
max_length=150,
required=True
)
class Meta:
model = ProfileModel
fields = [
"address",
"email",
"first_name",
"gravatar_enabled",
"last_name",
"middle_name",
"phone_number",
"photo",
"prefix",
"redirect_to",
"suffix",
"time_zone",
"username",
]
[docs] def __init__(self, *args, **kwargs):
"""Initialize with a user keyword argument, add redirect and time zone choices, and set defaults."""
# Get the user or die trying.
self.user = kwargs.pop("user", None)
if self.user is None:
raise ImproperlyConfigured("user is a required kwarg.")
# This allows view logic to pass the redirect choices but still default to those supplied by the accounts app.
redirect_to_choices = kwargs.pop(
"redirect_to_choices",
SUPERDJANGO.REDIRECT_TO_CHOICES or DEFAULT_REDIRECT_TO_CHOICES
)
# This allows view logic to pass time zone choices but still default to configured or common choices.
time_zone_choices = kwargs.pop(
"time_zone_choices",
SUPERDJANGO.TIMEZONE_CHOICES or COMMON_TIMEZONE_CHOICES
)
# Initialize the form.
super(ProfileForm, self).__init__(*args, **kwargs)
# Set the value of fields that come from the user.
self.fields['email'].initial = self.user.email
self.fields['username'].initial = self.user.username
# First and last name may have been entered if the user account was created manually or (for example) through a
# registration process that did not create a profile record. These will be blank during the first load of a
# profile form, but we can attempt to display them as initial values. Note that email and username above work
# because the values are not bound to profile. However,
# self.fields['first_name'].initial = profile.user.first_name will *not* work because those fields are bound
# in the call to super() above.
profile = kwargs['instance']
if not profile.first_name:
self.initial['first_name'] = profile.user.first_name
if not profile.last_name:
self.initial['last_name'] = profile.user.last_name
# Set choices.
self.fields['redirect_to'].choices = redirect_to_choices
self.fields['time_zone'].choices = time_zone_choices
[docs] def clean_email(self):
"""Email addresses must be unique. This checks to see if the current address is different than the submitted
address and makes sure a new address is not already taken.
"""
current_profile = get_user_profile(self.user)
if current_profile.user.email != self.cleaned_data['email']:
if UserModel.objects.filter(email__iexact=self.cleaned_data['email']).exists():
raise forms.ValidationError(_("This email address is already in use."))
return self.cleaned_data['email']
[docs] def clean_username(self):
"""User names must be unique. This checks to see if the current name is different than the submitted
name and makes sure a new name is not already taken.
"""
current_profile = get_user_profile(self.user)
current_username = getattr(current_profile.user, UserModel.USERNAME_FIELD)
if current_username != self.cleaned_data['username']:
criteria = {
UserModel.USERNAME_FIELD: self.cleaned_data['username']
}
if UserModel.objects.filter(**criteria).exists():
raise forms.ValidationError(_("This user name is already in use."))
return self.cleaned_data['username']
@property
def fieldsets(self):
"""Alias of ``get_fieldsets()``."""
return self.get_fieldsets()
[docs] def get_fieldsets(self):
"""Get the field sets to be included in the form.
:rtype: list[superdjango.html.library.Fieldset]
"""
a = list()
contact = Fieldset(_("Contact"), fields=[
self["prefix"],
self["first_name"],
self["middle_name"],
self["last_name"],
self["suffix"],
self["address"],
self["phone_number"],
self["email"],
])
a.append(contact)
user = Fieldset(_("User"), fields=[
self["username"],
self["photo"],
])
a.append(user)
prefs = Fieldset(_("Preferences"), fields=[
self["gravatar_enabled"],
self["redirect_to"],
self["time_zone"]
])
a.append(prefs)
return a
@property
def has_fieldsets(self):
"""Indicates the form has fieldsets.
:rtype: bool
"""
return True
[docs] def save(self, commit=True):
"""Process user fields."""
# Email is saved to the user account.
email = self.cleaned_data.pop('email')
# User names are also saved to the user account.
first_name = self.cleaned_data['first_name']
last_name = self.cleaned_data['last_name']
username = self.cleaned_data.pop('username')
user = UserModel.objects.get(pk=self.instance.user_id)
user.email = email
user.first_name = first_name
user.last_name = last_name
setattr(user, UserModel.USERNAME_FIELD, username)
user.save()
return super(ProfileForm, self).save(commit=commit)