Hide keyboard shortcuts

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 

2 

3from django.contrib.auth import REDIRECT_FIELD_NAME 

4from django.core.exceptions import ImproperlyConfigured 

5from django.http import Http404, HttpResponseRedirect 

6from django.urls import reverse, reverse_lazy 

7from django.utils.translation import ugettext_lazy as _ 

8from superdjango.conf import SUPERDJANGO 

9from superdjango.views import AccessMixin, BreadcrumbsMixin, MessageMixin, TemplateView, UpdateView, ViewSet 

10from ..utils import get_profile_model 

11from .forms import ProfileForm 

12from .signals import user_profile_links 

13 

14ProfileModel = get_profile_model() 

15 

16# Exports 

17 

18__all__ = ( 

19 "ProfileDetail", 

20 "ProfileRequiredMixin", 

21 "ProfileUpdate", 

22 "ProfileViewSet", 

23) 

24 

25# Mixins 

26 

27 

28class ProfileRequiredMixin(MessageMixin, AccessMixin): 

29 """Use this to require a profile before proceeding.""" 

30 

31 auto_create = SUPERDJANGO.USER_AUTO_CREATE_PROFILE 

32 """Indicates whether a profile should be created for the user if one does not exist.""" 

33 

34 login_required = True 

35 """A login is always required for profile access.""" 

36 

37 def check_other(self, request, *args, **kwargs): 

38 """Check whether a profile exists. Sets ``self.object`` as well. The ``auto_create`` option is passed to the 

39 profile model's ``for_user()`` method, which means the profile may be created on the fly. 

40 

41 A typical approach to this would be to define ``auto_create`` (or ``SUPERDJANGO_AUTO_CREATE_PROFILE``) as 

42 ``False`` for profile detail views and override to ``True`` for the profile update view. This allows profiles to 

43 be created as needed. 

44 """ 

45 if ProfileModel is None: 

46 raise ImproperlyConfigured("Profile view has been invoked without defining SUPERDJANGO_USER_PROFILE_MODEL.") 

47 

48 # noinspection PyUnresolvedReferences 

49 profile = ProfileModel.for_user(self.request.user, create=self.auto_create) 

50 

51 # noinspection PyAttributeOutsideInit 

52 self.object = profile 

53 

54 return profile is not None 

55 

56 def dispatch_other(self, request, message=None, redirect_url=None): 

57 """Returns a 404 response when ``raise_access_exception`` is ``True``. Otherwise, this will send authenticated 

58 users to the update view (where a profile should be created by default) and unauthenticated users to the login 

59 page. 

60 """ 

61 if self.raise_access_exception: 

62 raise Http404(_("A user profile is required to view this page, but no profile was found.")) 

63 

64 # Send authenticated users to profile update which (hopefully) allows them to create/update their profile. 

65 # Otherwise, redirect to login. This may result in an endless loop for the user if no auto create is enabled on 

66 # the profile update view. 

67 # noinspection PyUnresolvedReferences 

68 if self.request.user.is_authenticated: 

69 # noinspection PyUnresolvedReferences 

70 url = "%s?%s=%s" % (reverse("accounts_profile_update"), REDIRECT_FIELD_NAME, self.request.path) 

71 else: 

72 # noinspection PyUnresolvedReferences 

73 url = "%s?%s=%s" % (reverse("accounts_login"), REDIRECT_FIELD_NAME, self.request.path) 

74 

75 return HttpResponseRedirect(url) 

76 

77# Views 

78 

79 

80class ProfileDetail(ProfileRequiredMixin, BreadcrumbsMixin, TemplateView): 

81 """Display the user profile for the current user.""" 

82 active_page = "accounts" 

83 pattern_name = "accounts_profile" 

84 pattern_value = 'profile/' 

85 model = ProfileModel 

86 template_name = "accounts/profile_detail.html" 

87 

88 def get_breadcrumbs(self): 

89 """Get profile breadcrumbs.""" 

90 crumbs = super().get_breadcrumbs() 

91 

92 crumbs.add(_("Profile"), "") 

93 

94 return crumbs 

95 

96 def get_context_data(self, **kwargs): 

97 """Add ``profile`` to the context.""" 

98 context = super().get_context_data(**kwargs) 

99 

100 context['profile'] = self.object 

101 

102 groupings = list() 

103 links = list() 

104 results = user_profile_links.send(ProfileDetail, profile=self.object) 

105 for f, _links in results: 

106 for link in _links: 

107 if link.grouping is not None and link.grouping not in groupings: 

108 groupings.append(link.grouping) 

109 

110 links += _links 

111 

112 context['link_groupings'] = groupings 

113 context['links'] = links 

114 

115 return context 

116 

117 

118class ProfileUpdate(ProfileRequiredMixin, BreadcrumbsMixin, UpdateView): 

119 """Facilitate self management of user profile data.""" 

120 active_page = "accounts" 

121 auto_create = True 

122 form_class = ProfileForm 

123 pattern_name = "accounts_profile_update" 

124 pattern_value = 'profile/update/' 

125 model = ProfileModel 

126 success_url = reverse_lazy("accounts_profile") 

127 template_name = "accounts/profile_update_form.html" 

128 

129 def form_valid(self, form): 

130 if "time_zone" in form.cleaned_data: 

131 self.request.session[SUPERDJANGO.TIMEZONE_KEY] = form.cleaned_data['time_zone'] 

132 self.request.session.modified = True 

133 

134 return super().form_valid(form) 

135 

136 def get_breadcrumbs(self): 

137 """Overridden to so that ``UpdateView`` breadcrumbs are not used.""" 

138 crumbs = super().get_breadcrumbs() 

139 

140 crumbs.add(_("Profile"), reverse("accounts_profile")) 

141 crumbs.add(self.get_subtitle(), "") 

142 

143 return crumbs 

144 

145 def get_cancel_url(self): 

146 """Send the user back to the profile view.""" 

147 return reverse("accounts_profile") 

148 

149 def get_form(self, data=None, files=None, **kwargs): 

150 """Add the current user to form data.""" 

151 kwargs['user'] = self.request.user 

152 return super(ProfileUpdate, self).get_form(data=data, files=files, **kwargs) 

153 

154 def get_form_class(self): 

155 """Get the form class from the model or using the default ``form_class`` attribute of the view.""" 

156 try: 

157 return self.object.get_form_class() 

158 except AttributeError: 

159 return self.form_class 

160 

161 def get_object(self): 

162 """Overridden to return the current profile without a lookup key. ``self.object`` is defined in the 

163 ``ProfileRequiredMixin``. 

164 """ 

165 return self.object 

166 

167 def get_subtitle(self): 

168 """Provide a default subtitle for the page.""" 

169 if self.subtitle is not None: 

170 return self.subtitle 

171 

172 return _("Edit") 

173 

174# BaseView Sets 

175 

176 

177class ProfileViewSet(ViewSet): 

178 

179 views = [ 

180 ProfileDetail, 

181 ProfileUpdate, 

182 ]