# Imports
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext_lazy as _
from superdjango.conf import SUPERDJANGO
from superdjango.views import AccessMixin, BreadcrumbsMixin, MessageMixin, TemplateView, UpdateView, ViewSet
from ..utils import get_profile_model
from .forms import ProfileForm
from .signals import user_profile_links
ProfileModel = get_profile_model()
# Exports
__all__ = (
"ProfileDetail",
"ProfileRequiredMixin",
"ProfileUpdate",
"ProfileViewSet",
)
# Mixins
[docs]class ProfileRequiredMixin(MessageMixin, AccessMixin):
"""Use this to require a profile before proceeding."""
auto_create = SUPERDJANGO.USER_AUTO_CREATE_PROFILE
"""Indicates whether a profile should be created for the user if one does not exist."""
login_required = True
"""A login is always required for profile access."""
[docs] def check_other(self, request, *args, **kwargs):
"""Check whether a profile exists. Sets ``self.object`` as well. The ``auto_create`` option is passed to the
profile model's ``for_user()`` method, which means the profile may be created on the fly.
A typical approach to this would be to define ``auto_create`` (or ``SUPERDJANGO_AUTO_CREATE_PROFILE``) as
``False`` for profile detail views and override to ``True`` for the profile update view. This allows profiles to
be created as needed.
"""
if ProfileModel is None:
raise ImproperlyConfigured("Profile view has been invoked without defining SUPERDJANGO_USER_PROFILE_MODEL.")
# noinspection PyUnresolvedReferences
profile = ProfileModel.for_user(self.request.user, create=self.auto_create)
# noinspection PyAttributeOutsideInit
self.object = profile
return profile is not None
[docs] def dispatch_other(self, request, message=None, redirect_url=None):
"""Returns a 404 response when ``raise_access_exception`` is ``True``. Otherwise, this will send authenticated
users to the update view (where a profile should be created by default) and unauthenticated users to the login
page.
"""
if self.raise_access_exception:
raise Http404(_("A user profile is required to view this page, but no profile was found."))
# Send authenticated users to profile update which (hopefully) allows them to create/update their profile.
# Otherwise, redirect to login. This may result in an endless loop for the user if no auto create is enabled on
# the profile update view.
# noinspection PyUnresolvedReferences
if self.request.user.is_authenticated:
# noinspection PyUnresolvedReferences
url = "%s?%s=%s" % (reverse("accounts_profile_update"), REDIRECT_FIELD_NAME, self.request.path)
else:
# noinspection PyUnresolvedReferences
url = "%s?%s=%s" % (reverse("accounts_login"), REDIRECT_FIELD_NAME, self.request.path)
return HttpResponseRedirect(url)
# Views
[docs]class ProfileDetail(ProfileRequiredMixin, BreadcrumbsMixin, TemplateView):
"""Display the user profile for the current user."""
active_page = "accounts"
pattern_name = "accounts_profile"
pattern_value = 'profile/'
model = ProfileModel
template_name = "accounts/profile_detail.html"
[docs] def get_breadcrumbs(self):
"""Get profile breadcrumbs."""
crumbs = super().get_breadcrumbs()
crumbs.add(_("Profile"), "")
return crumbs
[docs] def get_context_data(self, **kwargs):
"""Add ``profile`` to the context."""
context = super().get_context_data(**kwargs)
context['profile'] = self.object
groupings = list()
links = list()
results = user_profile_links.send(ProfileDetail, profile=self.object)
for f, _links in results:
for link in _links:
if link.grouping is not None and link.grouping not in groupings:
groupings.append(link.grouping)
links += _links
context['link_groupings'] = groupings
context['links'] = links
return context
[docs]class ProfileUpdate(ProfileRequiredMixin, BreadcrumbsMixin, UpdateView):
"""Facilitate self management of user profile data."""
active_page = "accounts"
auto_create = True
form_class = ProfileForm
pattern_name = "accounts_profile_update"
pattern_value = 'profile/update/'
model = ProfileModel
success_url = reverse_lazy("accounts_profile")
template_name = "accounts/profile_update_form.html"
[docs] def get_breadcrumbs(self):
"""Overridden to so that ``UpdateView`` breadcrumbs are not used."""
crumbs = super().get_breadcrumbs()
crumbs.add(_("Profile"), reverse("accounts_profile"))
crumbs.add(self.get_subtitle(), "")
return crumbs
[docs] def get_cancel_url(self):
"""Send the user back to the profile view."""
return reverse("accounts_profile")
[docs] def get_object(self):
"""Overridden to return the current profile without a lookup key. ``self.object`` is defined in the
``ProfileRequiredMixin``.
"""
return self.object
[docs] def get_subtitle(self):
"""Provide a default subtitle for the page."""
if self.subtitle is not None:
return self.subtitle
return _("Edit")
# BaseView Sets
[docs]class ProfileViewSet(ViewSet):
views = [
ProfileDetail,
ProfileUpdate,
]