Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django import forms
4from django.conf import settings
5from django.contrib.auth import get_user_model
6from django.core.exceptions import ImproperlyConfigured
7from django.utils.translation import ugettext_lazy as _
8from superdjango.conf import SUPERDJANGO
9from superdjango.html.library import Fieldset
10from superdjango.contrib.i18n.timezones import COMMON_TIMEZONE_CHOICES
11from ..utils import get_profile_model, get_user_profile
13ProfileModel = get_profile_model()
14UserModel = get_user_model()
16# Exports
18__all__ = (
19 "ProfileForm",
20)
22# Constants
24DEFAULT_REDIRECT_TO_CHOICES = (
25 ("/", _("Home")),
26 ("/accounts/profile/", _("My Account")),
27)
29LOGIN_REDIRECT_URL = getattr(settings, "LOGIN_REDIRECT_URL", "/")
31# Forms
34class ProfileForm(forms.ModelForm):
35 """A base form for user profiles based on the ``ProfileModel``."""
37 field_order = [
38 "prefix",
39 "first_name",
40 "last_name",
41 "suffix",
42 "address",
43 "phone_number",
44 "email",
45 "username",
46 "redirect_to",
47 "time_zone",
48 "gravatar_enabled",
49 "photo",
50 ]
52 address = forms.CharField(
53 label=_("Address"),
54 help_text=_("Enter your mailing address."),
55 widget=forms.Textarea,
56 required=False
57 )
59 email = forms.EmailField(
60 label=_("Email Address"),
61 help_text=_("Enter your email address."),
62 required=False
63 )
65 first_name = forms.CharField(
66 label=_("First Name"),
67 help_text=_("Your given name."),
68 max_length=128,
69 required=True
70 )
72 gravatar_enabled = forms.BooleanField(
73 help_text=_("Indicates whether to use gravatar.com for your profile photos."),
74 label=_("Enable Gravatar")
75 )
77 last_name = forms.CharField(
78 label=_("Last Name"),
79 help_text=_("Your family name."),
80 max_length=128,
81 required=True
82 )
84 middle_name = forms.CharField(
85 label=_("Middle Name"),
86 help_text=_("Your middle name or initial."),
87 max_length=128,
88 required=False
89 )
91 phone_number = forms.CharField(
92 label=_("Phone Number"),
93 help_text=_("Enter your phone number."),
94 required=False
95 )
97 photo = forms.FileField(
98 label=_("Photo"),
99 help_text=_("Upload a photo to use for your profile picture."),
100 required=False
101 )
103 prefix = forms.CharField(
104 label=_("Prefix"),
105 help_text=_("Enter a prefix to appear before your name or leave blank for nothing."),
106 max_length=64,
107 required=False
108 )
110 # Default redirect choices will be overridden by account settings.
111 redirect_to = forms.ChoiceField(
112 choices=SUPERDJANGO.REDIRECT_TO_CHOICES or DEFAULT_REDIRECT_TO_CHOICES,
113 initial=LOGIN_REDIRECT_URL,
114 help_text=_("By default, go to this page after logging in."),
115 label=_("Redirect To"),
116 required=True
117 )
118 suffix = forms.CharField(
119 label=_("Suffix"),
120 help_text=_("Enter a suffix to appear after your name or leave blank for nothing."),
121 max_length=64,
122 required=False
123 )
125 # Default choices will be overridden by account settings.
126 time_zone = forms.ChoiceField(
127 choices=SUPERDJANGO.TIMEZONE_CHOICES or COMMON_TIMEZONE_CHOICES,
128 label=_("Timezone"),
129 help_text=_("Select the timezone in which you reside or work."),
130 required=False
131 )
133 username = forms.CharField(
134 label=_("User Name"),
135 max_length=150,
136 required=True
137 )
139 class Meta:
140 model = ProfileModel
141 fields = [
142 "address",
143 "email",
144 "first_name",
145 "gravatar_enabled",
146 "last_name",
147 "middle_name",
148 "phone_number",
149 "photo",
150 "prefix",
151 "redirect_to",
152 "suffix",
153 "time_zone",
154 "username",
155 ]
157 def __init__(self, *args, **kwargs):
158 """Initialize with a user keyword argument, add redirect and time zone choices, and set defaults."""
159 # Get the user or die trying.
160 self.user = kwargs.pop("user", None)
161 if self.user is None:
162 raise ImproperlyConfigured("user is a required kwarg.")
164 # This allows view logic to pass the redirect choices but still default to those supplied by the accounts app.
165 redirect_to_choices = kwargs.pop(
166 "redirect_to_choices",
167 SUPERDJANGO.REDIRECT_TO_CHOICES or DEFAULT_REDIRECT_TO_CHOICES
168 )
170 # This allows view logic to pass time zone choices but still default to configured or common choices.
171 time_zone_choices = kwargs.pop(
172 "time_zone_choices",
173 SUPERDJANGO.TIMEZONE_CHOICES or COMMON_TIMEZONE_CHOICES
174 )
176 # Initialize the form.
177 super(ProfileForm, self).__init__(*args, **kwargs)
179 # Set the value of fields that come from the user.
180 self.fields['email'].initial = self.user.email
181 self.fields['username'].initial = self.user.username
183 # First and last name may have been entered if the user account was created manually or (for example) through a
184 # registration process that did not create a profile record. These will be blank during the first load of a
185 # profile form, but we can attempt to display them as initial values. Note that email and username above work
186 # because the values are not bound to profile. However,
187 # self.fields['first_name'].initial = profile.user.first_name will *not* work because those fields are bound
188 # in the call to super() above.
189 profile = kwargs['instance']
190 if not profile.first_name:
191 self.initial['first_name'] = profile.user.first_name
193 if not profile.last_name:
194 self.initial['last_name'] = profile.user.last_name
196 # Set choices.
197 self.fields['redirect_to'].choices = redirect_to_choices
198 self.fields['time_zone'].choices = time_zone_choices
200 def clean_email(self):
201 """Email addresses must be unique. This checks to see if the current address is different than the submitted
202 address and makes sure a new address is not already taken.
203 """
204 current_profile = get_user_profile(self.user)
206 if current_profile.user.email != self.cleaned_data['email']:
207 if UserModel.objects.filter(email__iexact=self.cleaned_data['email']).exists():
208 raise forms.ValidationError(_("This email address is already in use."))
210 return self.cleaned_data['email']
212 def clean_username(self):
213 """User names must be unique. This checks to see if the current name is different than the submitted
214 name and makes sure a new name is not already taken.
215 """
216 current_profile = get_user_profile(self.user)
218 current_username = getattr(current_profile.user, UserModel.USERNAME_FIELD)
220 if current_username != self.cleaned_data['username']:
221 criteria = {
222 UserModel.USERNAME_FIELD: self.cleaned_data['username']
223 }
224 if UserModel.objects.filter(**criteria).exists():
225 raise forms.ValidationError(_("This user name is already in use."))
227 return self.cleaned_data['username']
229 @property
230 def fieldsets(self):
231 """Alias of ``get_fieldsets()``."""
232 return self.get_fieldsets()
234 def get_fieldsets(self):
235 """Get the field sets to be included in the form.
237 :rtype: list[superdjango.html.library.Fieldset]
239 """
240 a = list()
242 contact = Fieldset(_("Contact"), fields=[
243 self["prefix"],
244 self["first_name"],
245 self["middle_name"],
246 self["last_name"],
247 self["suffix"],
248 self["address"],
249 self["phone_number"],
250 self["email"],
251 ])
252 a.append(contact)
254 user = Fieldset(_("User"), fields=[
255 self["username"],
256 self["photo"],
257 ])
258 a.append(user)
260 prefs = Fieldset(_("Preferences"), fields=[
261 self["gravatar_enabled"],
262 self["redirect_to"],
263 self["time_zone"]
264 ])
265 a.append(prefs)
267 return a
269 @property
270 def has_fieldsets(self):
271 """Indicates the form has fieldsets.
273 :rtype: bool
275 """
276 return True
278 def save(self, commit=True):
279 """Process user fields."""
281 # Email is saved to the user account.
282 email = self.cleaned_data.pop('email')
284 # User names are also saved to the user account.
285 first_name = self.cleaned_data['first_name']
286 last_name = self.cleaned_data['last_name']
287 username = self.cleaned_data.pop('username')
289 user = UserModel.objects.get(pk=self.instance.user_id)
290 user.email = email
291 user.first_name = first_name
292 user.last_name = last_name
293 setattr(user, UserModel.USERNAME_FIELD, username)
294 user.save()
296 return super(ProfileForm, self).save(commit=commit)