summaryrefslogtreecommitdiffstats
path: root/profiles
diff options
context:
space:
mode:
authorsubstantialnoninfringinguser <substantialnoninfringinguser@gmail.com>2009-05-13 05:48:10 +0100
committersubstantialnoninfringinguser <substantialnoninfringinguser@gmail.com>2009-05-13 05:48:10 +0100
commited345f25760d8927f834a69202c2b9b2cef71ee0 (patch)
tree652dba01640060cc2106af850955728828bcd8f0 /profiles
parentcdd4e685ee95e44b9a599b03cf11723a4ce7b7c6 (diff)
downloadtroggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.tar.gz
troggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.tar.bz2
troggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.zip
[svn] Add user registration and user profiles.
Used modified versions of django-registration and django-profiles , both on bitbucket. The Person model is now set up as the profile for auth.User s. I set up a requestcontext so that settings is automatically passed to every template, no need to repeat ourselves in views. However, this needs to be refined: I will soon change it to only pass a subset of settings. E.G. we do not need to be passing the DB login and password! Copied from http://cucc@cucc.survex.com/svn/trunk/expoweb/troggle/, rev. 8231 by aaron @ 1/29/2009 11:02 PM
Diffstat (limited to 'profiles')
-rw-r--r--profiles/__init__.py0
-rw-r--r--profiles/urls.py43
-rw-r--r--profiles/utils.py45
-rw-r--r--profiles/views.py340
4 files changed, 428 insertions, 0 deletions
diff --git a/profiles/__init__.py b/profiles/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/profiles/__init__.py
diff --git a/profiles/urls.py b/profiles/urls.py
new file mode 100644
index 0000000..05ff9e4
--- /dev/null
+++ b/profiles/urls.py
@@ -0,0 +1,43 @@
+"""
+URLConf for Django user profile management.
+
+Recommended usage is to use a call to ``include()`` in your project's
+root URLConf to include this URLConf for any URL beginning with
+'/profiles/'.
+
+If the default behavior of the profile views is acceptable to you,
+simply use a line like this in your root URLConf to set up the default
+URLs for profiles::
+
+ (r'^profiles/', include('profiles.urls')),
+
+But if you'd like to customize the behavior (e.g., by passing extra
+arguments to the various views) or split up the URLs, feel free to set
+up your own URL patterns for these views instead. If you do, it's a
+good idea to keep the name ``profiles_profile_detail`` for the pattern
+which points to the ``profile_detail`` view, since several views use
+``reverse()`` with that name to generate a default post-submission
+redirect. If you don't use that name, remember to explicitly pass
+``success_url`` to those views.
+
+"""
+
+from django.conf.urls.defaults import *
+
+from profiles import views
+
+
+urlpatterns = patterns('',
+ url(r'^create/$',
+ views.create_profile,
+ name='profiles_create_profile'),
+ url(r'^edit/$',
+ views.edit_profile,
+ name='profiles_edit_profile'),
+ url(r'^(?P<username>\w+)/$',
+ views.profile_detail,
+ name='profiles_profile_detail'),
+ url(r'^$',
+ views.profile_list,
+ name='profiles_profile_list'),
+ )
diff --git a/profiles/utils.py b/profiles/utils.py
new file mode 100644
index 0000000..faacfcb
--- /dev/null
+++ b/profiles/utils.py
@@ -0,0 +1,45 @@
+"""
+Utility functions for retrieving and generating forms for the
+site-specific user profile model specified in the
+``AUTH_PROFILE_MODULE`` setting.
+
+"""
+
+from django import forms
+from django.conf import settings
+from django.contrib.auth.models import SiteProfileNotAvailable
+from django.db.models import get_model
+
+
+def get_profile_model():
+ """
+ Return the model class for the currently-active user profile
+ model, as defined by the ``AUTH_PROFILE_MODULE`` setting. If that
+ setting is missing, raise
+ ``django.contrib.auth.models.SiteProfileNotAvailable``.
+
+ """
+ if (not hasattr(settings, 'AUTH_PROFILE_MODULE')) or \
+ (not settings.AUTH_PROFILE_MODULE):
+ raise SiteProfileNotAvailable
+ profile_mod = get_model(*settings.AUTH_PROFILE_MODULE.split('.'))
+ if profile_mod is None:
+ raise SiteProfileNotAvailable
+ return profile_mod
+
+
+def get_profile_form():
+ """
+ Return a form class (a subclass of the default ``ModelForm``)
+ suitable for creating/editing instances of the site-specific user
+ profile model, as defined by the ``AUTH_PROFILE_MODULE``
+ setting. If that setting is missing, raise
+ ``django.contrib.auth.models.SiteProfileNotAvailable``.
+
+ """
+ profile_mod = get_profile_model()
+ class _ProfileForm(forms.ModelForm):
+ class Meta:
+ model = profile_mod
+ exclude = ('user',) # User will be filled in by the view.
+ return _ProfileForm
diff --git a/profiles/views.py b/profiles/views.py
new file mode 100644
index 0000000..c119165
--- /dev/null
+++ b/profiles/views.py
@@ -0,0 +1,340 @@
+"""
+Views for creating, editing and viewing site-specific user profiles.
+
+"""
+
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.urlresolvers import reverse
+from django.http import Http404
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.views.generic.list_detail import object_list
+
+from profiles import utils
+
+import troggle.settings as settings
+
+
+
+def create_profile(request, form_class=None, success_url=None,
+ template_name='profiles/create_profile.html',
+ extra_context=None):
+ """
+ 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.
+
+ **Optional arguments:**
+
+ ``extra_context``
+ A dictionary of variables to add to the template context. Any
+ callable object in this dictionary will be called to produce
+ the end result which appears in the context.
+
+ ``form_class``
+ The form class to use for validating and creating the user
+ profile. This form class must define a method named
+ ``save()``, implementing the same argument signature as the
+ ``save()`` method of a standard Django ``ModelForm`` (this
+ view will call ``save(commit=False)`` to obtain the profile
+ object, and fill in the user before the final save). If the
+ profile object includes many-to-many relations, the convention
+ established by ``ModelForm`` of using a method named
+ ``save_m2m()`` will be used, and so your form class should
+ also define this method.
+
+ If this argument is not supplied, this view will use a
+ ``ModelForm`` automatically generated from the model specified
+ by ``AUTH_PROFILE_MODULE``.
+
+ ``success_url``
+ The URL to redirect to after successful profile creation. If
+ this argument is not supplied, this will default to the URL of
+ :view:`profiles.views.profile_detail` for the newly-created
+ profile object.
+
+ ``template_name``
+ The template to use when displaying the profile-creation
+ form. If not supplied, this will default to
+ :template:`profiles/create_profile.html`.
+
+ **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
+
+ #
+ # We set up success_url here, rather than as the default value for
+ # the argument. Trying to do it as the argument's default would
+ # mean evaluating the call to reverse() at the time this module is
+ # first imported, which introduces a circular dependency: to
+ # perform the reverse lookup we need access to profiles/urls.py,
+ # but profiles/urls.py in turn imports this module.
+ #
+
+ 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.save()
+ if hasattr(form, 'save_m2m'):
+ form.save_m2m()
+ return HttpResponseRedirect(success_url)
+ else:
+ form = form_class()
+
+ if extra_context is None:
+ extra_context = {}
+ context = RequestContext(request)
+ for key, value in extra_context.items():
+ context[key] = callable(value) and value() or value
+
+ return render_to_response(template_name,
+ { 'form': form, 'settings':settings },
+ context_instance=context)
+create_profile = login_required(create_profile)
+
+def edit_profile(request, form_class=None, success_url=None,
+ template_name='profiles/edit_profile.html',
+ extra_context=None):
+ """
+ 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.
+
+ **Optional arguments:**
+
+ ``extra_context``
+ A dictionary of variables to add to the template context. Any
+ callable object in this dictionary will be called to produce
+ the end result which appears in the context.
+
+ ``form_class``
+ The form class to use for validating and editing the user
+ profile. This form class must operate similarly to a standard
+ Django ``ModelForm`` in that it must accept an instance of the
+ object to be edited as the keyword argument ``instance`` to
+ its constructor, and it must implement a method named
+ ``save()`` which will save the updates to the object. If this
+ argument is not specified, this view will use a ``ModelForm``
+ generated from the model specified in the
+ ``AUTH_PROFILE_MODULE`` setting.
+
+ ``success_url``
+ The URL to redirect to following a successful edit. If not
+ specified, this will default to the URL of
+ :view:`profiles.views.profile_detail` for the profile object
+ being edited.
+
+ ``template_name``
+ The template to use when displaying the profile-editing
+ form. If not specified, this will default to
+ :template:`profiles/edit_profile.html`.
+
+ **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'))
+
+ #
+ # See the comment in create_profile() for discussion of why
+ # success_url is set up here, rather than as a default value for
+ # the argument.
+ #
+
+ 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)
+
+ if extra_context is None:
+ extra_context = {}
+ context = RequestContext(request)
+ for key, value in extra_context.items():
+ context[key] = callable(value) and value() or value
+
+ return render_to_response(template_name,
+ { 'form': form,
+ 'profile': profile_obj, },
+ context_instance=context)
+edit_profile = login_required(edit_profile)
+
+def profile_detail(request, username, public_profile_field=None,
+ template_name='profiles/profile_detail.html',
+ extra_context=None):
+ """
+ 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.
+
+ **Required arguments:**
+
+ ``username``
+ The username of the user whose profile is being displayed.
+
+ **Optional arguments:**
+
+ ``extra_context``
+ A dictionary of variables to add to the template context. Any
+ callable object in this dictionary will be called to produce
+ the end result which appears in the context.
+
+ ``public_profile_field``
+ The name of a ``BooleanField`` on the profile model; if the
+ value of that field on the user's profile is ``False``, the
+ ``profile`` variable in the template will be ``None``. Use
+ this feature to allow users to mark their profiles as not
+ being publicly viewable.
+
+ If this argument is not specified, it will be assumed that all
+ users' profiles are publicly viewable.
+
+ ``template_name``
+ The name of the template to use for displaying the profile. If
+ not specified, this will default to
+ :template:`profiles/profile_detail.html`.
+
+ **Context:**
+
+ ``profile``
+ The user's profile, or ``None`` if the user's profile is not
+ publicly viewable (see the description of
+ ``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
+
+ if extra_context is None:
+ extra_context = {}
+ context = RequestContext(request)
+ for key, value in extra_context.items():
+ context[key] = callable(value) and value() or value
+
+ return render_to_response(template_name,
+ { 'profile': profile_obj },
+ context_instance=context)
+
+def profile_list(request, public_profile_field=None,
+ template_name='profiles/profile_list.html', **kwargs):
+ """
+ A 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.
+
+ **Optional arguments:**
+
+ ``public_profile_field``
+ The name of a ``BooleanField`` on the profile model; if the
+ value of that field on a user's profile is ``False``, that
+ profile will be excluded from the list. Use this feature to
+ allow users to mark their profiles as not being publicly
+ viewable.
+
+ If this argument is not specified, it will be assumed that all
+ users' profiles are publicly viewable.
+
+ ``template_name``
+ The name of the template to use for displaying the profiles. If
+ not specified, this will default to
+ :template:`profiles/profile_list.html`.
+
+ Additionally, all arguments accepted by the
+ :view:`django.views.generic.list_detail.object_list` generic view
+ will be accepted here, and applied in the same fashion, with one
+ exception: ``queryset`` will always be the ``QuerySet`` of the
+ model specified by the ``AUTH_PROFILE_MODULE`` setting, optionally
+ filtered to remove non-publicly-viewable proiles.
+
+ **Context:**
+
+ Same as the :view:`django.views.generic.list_detail.object_list`
+ generic view.
+
+ **Template:**
+
+ ``template_name`` keyword argument or
+ :template:`profiles/profile_list.html`.
+
+ """
+ profile_model = utils.get_profile_model()
+ queryset = profile_model._default_manager.all()
+ if public_profile_field is not None:
+ queryset = queryset.filter(**{ public_profile_field: True })
+ kwargs['queryset'] = queryset
+ return object_list(request, template_name=template_name, **kwargs)