""" Views for creating, editing and viewing site-specific user profiles. """ from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, render_to_response from django.template import RequestContext from django.views.generic.list_detail import object_list from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from profiles import utils def create_profile(request, form_class=None, success_url=None, template_name='profiles/create_profile.html'): """ Create a profile for the current user, if one doesn't already exist. If the user already has a profile, as determined by ``request.user.get_profile()``, a redirect will be issued to the :view:`profiles.views.edit_profile` view. If no profile model has been specified in the ``AUTH_PROFILE_MODULE`` setting, ``django.contrib.auth.models.SiteProfileNotAvailable`` will be raised. To specify the form class used for profile creation, pass it as the keyword argument ``form_class``; if this is not supplied, it will fall back to a ``ModelForm`` for the model specified in the ``AUTH_PROFILE_MODULE`` setting. If you are supplying your own form class, it must define a method named ``save()`` which corresponds to the signature of ``save()`` on ``ModelForm``, because this view will call it with ``commit=False`` and then fill in the relationship to the user (which must be via a field on the profile model named ``user``, a requirement already imposed by ``User.get_profile()``) before finally saving the profile object. If many-to-many relations are involved, the convention established by ``ModelForm`` of looking for a ``save_m2m()`` method on the form is used, and so your form class should define this method. To specify a URL to redirect to after successful profile creation, pass it as the keyword argument ``success_url``; this will default to the URL of the :view:`profiles.views.profile_detail` view for the new profile if unspecified. To specify the template to use, pass it as the keyword argument ``template_name``; this will default to :template:`profiles/create_profile.html` if unspecified. Context: form The profile-creation form. Template: ``template_name`` keyword argument, or :template:`profiles/create_profile.html`. """ try: profile_obj = request.user.get_profile() return HttpResponseRedirect(reverse('profiles_edit_profile')) except ObjectDoesNotExist: pass if success_url is None: success_url = reverse('profiles_profile_detail', kwargs={ 'username': request.user.username }) if form_class is None: form_class = utils.get_profile_form() if request.method == 'POST': form = form_class(data=request.POST, files=request.FILES) if form.is_valid(): profile_obj = form.save(commit=False) profile_obj.user = request.user profile_obj.city = request.method['POST'].get("city") profile_obj.country = request.method['POST'].get("country") profile_obj.save() if hasattr(form, 'save_m2m'): form.save_m2m() return HttpResponseRedirect(success_url) else: form = form_class() return render_to_response(template_name, { 'form': form }, context_instance=RequestContext(request)) create_profile = login_required(create_profile) def edit_profile(request, form_class=None, success_url=None, template_name='profiles/edit_profile.html'): """ Edit the current user's profile. If the user does not already have a profile (as determined by ``User.get_profile()``), a redirect will be issued to the :view:`profiles.views.create_profile` view; if no profile model has been specified in the ``AUTH_PROFILE_MODULE`` setting, ``django.contrib.auth.models.SiteProfileNotAvailable`` will be raised. To specify the form class used for profile editing, pass it as the keyword argument ``form_class``; this form class must have a ``save()`` method which will save updates to the profile object. If not supplied, this will default to a ``ModelForm`` for the profile model. If you supply a form class, its ``__init__()`` method must accept an instance of the profile model as the keyword argument ``instance``. To specify the URL to redirect to following a successful edit, pass it as the keyword argument ``success_url``; this will default to the URL of the :view:`profiles.views.profile_detail` view if not supplied. To specify the template to use, pass it as the keyword argument ``template_name``; this will default to :template:`profiles/edit_profile.html` if not supplied. Context: form The form for editing the profile. profile The user's current profile. Template: ``template_name`` keyword argument or :template:`profiles/edit_profile.html`. """ try: profile_obj = request.user.get_profile() except ObjectDoesNotExist: return HttpResponseRedirect(reverse('profiles_create_profile')) if success_url is None: success_url = reverse('profiles_profile_detail', kwargs={ 'username': request.user.username }) if form_class is None: form_class = utils.get_profile_form() if request.method == 'POST': form = form_class(data=request.POST, files=request.FILES, instance=profile_obj) if form.is_valid(): form.save() return HttpResponseRedirect(success_url) else: form = form_class(instance=profile_obj) return render_to_response(template_name, { 'form': form, 'profile': profile_obj, }, context_instance=RequestContext(request)) edit_profile = login_required(edit_profile) def profile_detail(request, username, public_profile_field=None, template_name='profiles/profile_detail.html'): """ Detail view of a user's profile. If no profile model has been specified in the ``AUTH_PROFILE_MODULE`` setting, ``django.contrib.auth.models.SiteProfileNotAvailable`` will be raised. If the user has not yet created a profile, ``Http404`` will be raised. If a field on the profile model determines whether the profile can be publicly viewed, pass the name of that field (as a string) as the keyword argument ``public_profile_field``; that attribute will be checked before displaying the profile, and if it does not return a ``True`` value, the ``profile`` variable in the template will be ``None``. As a result, this field must be a ``BooleanField``. To specify the template to use, pass it as the keyword argument ``template_name``; this will default to :template:`profiles/profile_detail.html` if not supplied. Context: profile The user's profile, or ``None`` if the user's profile is not publicly viewable (see the note about ``public_profile_field`` above). Template: ``template_name`` keyword argument or :template:`profiles/profile_detail.html`. """ user = get_object_or_404(User, username=username) try: profile_obj = user.get_profile() except ObjectDoesNotExist: raise Http404 if public_profile_field is not None and \ not getattr(profile_obj, public_profile_field): profile_obj = None return render_to_response(template_name, { 'profile': profile_obj }, context_instance=RequestContext(request)) def profile_list(request, public_profile_field=None, template_name='profiles/profile_list.html', **kwargs): """ List of user profiles. If no profile model has been specified in the ``AUTH_PROFILE_MODULE`` setting, ``django.contrib.auth.models.SiteProfileNotAvailable`` will be raised. If a field on the profile model determines whether the profile can be publicly viewed, pass the name of that field as the keyword argument ``public_profile_field``; the ``QuerySet`` of profiles will be filtered to include only those on which that field is ``True`` (as a result, this field must be a ``BooleanField``). This view is a wrapper around the :view:`django.views.generic.list_detail.object_list` generic view, so any arguments which are legal for that view will be accepted, with the exception of ``queryset``, which will always be set to the default ``QuerySet`` of the profile model, optionally filtered as described above. Template: ``template_name`` keyword argument or :template:`profiles/profile_list.html`. Context: Same as the :view:`django.views.generic.list_detail.object_list` generic view. """ profile_model = utils.get_profile_model() if 'queryset' in kwargs: del kwargs['queryset'] queryset = profile_model._default_manager.all() if public_profile_field is not None: queryset = queryset.filter(**{ public_profile_field: True }) return object_list(request, queryset=queryset, template_name=template_name, **kwargs)