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.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
14ProfileModel = get_profile_model()
16# Exports
18__all__ = (
19 "ProfileDetail",
20 "ProfileRequiredMixin",
21 "ProfileUpdate",
22 "ProfileViewSet",
23)
25# Mixins
28class ProfileRequiredMixin(MessageMixin, AccessMixin):
29 """Use this to require a profile before proceeding."""
31 auto_create = SUPERDJANGO.USER_AUTO_CREATE_PROFILE
32 """Indicates whether a profile should be created for the user if one does not exist."""
34 login_required = True
35 """A login is always required for profile access."""
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.
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.")
48 # noinspection PyUnresolvedReferences
49 profile = ProfileModel.for_user(self.request.user, create=self.auto_create)
51 # noinspection PyAttributeOutsideInit
52 self.object = profile
54 return profile is not None
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."))
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)
75 return HttpResponseRedirect(url)
77# Views
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"
88 def get_breadcrumbs(self):
89 """Get profile breadcrumbs."""
90 crumbs = super().get_breadcrumbs()
92 crumbs.add(_("Profile"), "")
94 return crumbs
96 def get_context_data(self, **kwargs):
97 """Add ``profile`` to the context."""
98 context = super().get_context_data(**kwargs)
100 context['profile'] = self.object
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)
110 links += _links
112 context['link_groupings'] = groupings
113 context['links'] = links
115 return context
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"
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
134 return super().form_valid(form)
136 def get_breadcrumbs(self):
137 """Overridden to so that ``UpdateView`` breadcrumbs are not used."""
138 crumbs = super().get_breadcrumbs()
140 crumbs.add(_("Profile"), reverse("accounts_profile"))
141 crumbs.add(self.get_subtitle(), "")
143 return crumbs
145 def get_cancel_url(self):
146 """Send the user back to the profile view."""
147 return reverse("accounts_profile")
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)
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
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
167 def get_subtitle(self):
168 """Provide a default subtitle for the page."""
169 if self.subtitle is not None:
170 return self.subtitle
172 return _("Edit")
174# BaseView Sets
177class ProfileViewSet(ViewSet):
179 views = [
180 ProfileDetail,
181 ProfileUpdate,
182 ]