[cig-commits] r11897 - cs/portal/trunk/northridge/SeismoWebPortal
leif at geodynamics.org
leif at geodynamics.org
Thu May 1 20:14:00 PDT 2008
Author: leif
Date: 2008-05-01 20:14:00 -0700 (Thu, 01 May 2008)
New Revision: 11897
Added:
cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py
Modified:
cs/portal/trunk/northridge/SeismoWebPortal/urls.py
cs/portal/trunk/northridge/SeismoWebPortal/views.py
Log:
Introduced the mezzanine layer, which will allow the replacement of
all CRUD views with a single function (see prototype implementation in
'root'). Initially, this module is simply a concatenation of Django's
'create_update' and 'object_list' modules, pushed inside a class.
Added: cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py (rev 0)
+++ cs/portal/trunk/northridge/SeismoWebPortal/mezzanine.py 2008-05-02 03:14:00 UTC (rev 11897)
@@ -0,0 +1,338 @@
+
+
+from django.core.xheaders import populate_xheaders
+from django.template import loader, RequestContext
+from django import oldforms
+from django.db.models import FileField
+from django.contrib.auth.views import redirect_to_login
+from django.template import RequestContext
+from django.http import Http404, HttpResponse, HttpResponseRedirect
+from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
+from django.core.xheaders import populate_xheaders
+from django.core.paginator import ObjectPaginator, InvalidPage
+from django.utils.translation import gettext
+
+
+class Object(object):
+
+ @classmethod
+ def create(cls, request, model, template_name=None,
+ template_loader=loader, extra_context=None, post_save_redirect=None,
+ login_required=False, follow=None, context_processors=None):
+ """
+ Generic object-creation function.
+
+ Templates: ``<app_label>/<model_name>_form.html``
+ Context:
+ form
+ the form wrapper for the object
+ """
+ if extra_context is None: extra_context = {}
+ if login_required and not request.user.is_authenticated():
+ return redirect_to_login(request.path)
+
+ manipulator = model.AddManipulator(follow=follow)
+ if request.POST:
+ # If data was POSTed, we're trying to create a new object
+ new_data = request.POST.copy()
+
+ if model._meta.has_field_type(FileField):
+ new_data.update(request.FILES)
+
+ # Check for errors
+ errors = manipulator.get_validation_errors(new_data)
+ manipulator.do_html2python(new_data)
+
+ if not errors:
+ # No errors -- this means we can save the data!
+ new_object = manipulator.save(new_data)
+
+ if request.user.is_authenticated():
+ request.user.message_set.create(message=gettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name})
+
+ # Redirect to the new object: first by trying post_save_redirect,
+ # then by obj.get_absolute_url; fail if neither works.
+ if post_save_redirect:
+ return HttpResponseRedirect(post_save_redirect % new_object.__dict__)
+ elif hasattr(new_object, 'get_absolute_url'):
+ return HttpResponseRedirect(new_object.get_absolute_url())
+ else:
+ raise ImproperlyConfigured("No URL to redirect to from generic create view.")
+ else:
+ # No POST, so we want a brand new form without any data or errors
+ errors = {}
+ new_data = manipulator.flatten_data()
+
+ # Create the FormWrapper, template, context, response
+ form = oldforms.FormWrapper(manipulator, new_data, errors)
+ if not template_name:
+ template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ 'form': form,
+ }, context_processors)
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ return HttpResponse(t.render(c))
+
+ def update(self, request, model, object_id=None, slug=None,
+ slug_field=None, template_name=None, template_loader=loader,
+ extra_context=None, post_save_redirect=None,
+ login_required=False, follow=None, context_processors=None,
+ template_object_name='object'):
+ """
+ Generic object-update function.
+
+ Templates: ``<app_label>/<model_name>_form.html``
+ Context:
+ form
+ the form wrapper for the object
+ object
+ the original object being edited
+ """
+ if extra_context is None: extra_context = {}
+ if login_required and not request.user.is_authenticated():
+ return redirect_to_login(request.path)
+
+ # Look up the object to be edited
+ lookup_kwargs = {}
+ if object_id:
+ lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
+ elif slug and slug_field:
+ lookup_kwargs['%s__exact' % slug_field] = slug
+ else:
+ raise AttributeError("Generic edit view must be called with either an object_id or a slug/slug_field")
+ try:
+ object = model.objects.get(**lookup_kwargs)
+ except ObjectDoesNotExist:
+ raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
+
+ manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)
+
+ if request.POST:
+ new_data = request.POST.copy()
+ if model._meta.has_field_type(FileField):
+ new_data.update(request.FILES)
+ errors = manipulator.get_validation_errors(new_data)
+ manipulator.do_html2python(new_data)
+ if not errors:
+ object = manipulator.save(new_data)
+
+ if request.user.is_authenticated():
+ request.user.message_set.create(message=gettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name})
+
+ # Do a post-after-redirect so that reload works, etc.
+ if post_save_redirect:
+ return HttpResponseRedirect(post_save_redirect % object.__dict__)
+ elif hasattr(object, 'get_absolute_url'):
+ return HttpResponseRedirect(object.get_absolute_url())
+ else:
+ raise ImproperlyConfigured("No URL to redirect to from generic create view.")
+ else:
+ errors = {}
+ # This makes sure the form acurate represents the fields of the place.
+ new_data = manipulator.flatten_data()
+
+ form = oldforms.FormWrapper(manipulator, new_data, errors)
+ if not template_name:
+ template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ 'form': form,
+ template_object_name: object,
+ }, context_processors)
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ response = HttpResponse(t.render(c))
+ populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
+ return response
+
+ def delete(self, request, model, post_delete_redirect,
+ object_id=None, slug=None, slug_field=None, template_name=None,
+ template_loader=loader, extra_context=None,
+ login_required=False, context_processors=None, template_object_name='object'):
+ """
+ Generic object-delete function.
+
+ The given template will be used to confirm deletetion if this view is
+ fetched using GET; for safty, deletion will only be performed if this
+ view is POSTed.
+
+ Templates: ``<app_label>/<model_name>_confirm_delete.html``
+ Context:
+ object
+ the original object being deleted
+ """
+ if extra_context is None: extra_context = {}
+ if login_required and not request.user.is_authenticated():
+ return redirect_to_login(request.path)
+
+ # Look up the object to be edited
+ lookup_kwargs = {}
+ if object_id:
+ lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
+ elif slug and slug_field:
+ lookup_kwargs['%s__exact' % slug_field] = slug
+ else:
+ raise AttributeError("Generic delete view must be called with either an object_id or a slug/slug_field")
+ try:
+ object = model._default_manager.get(**lookup_kwargs)
+ except ObjectDoesNotExist:
+ raise Http404, "No %s found for %s" % (model._meta.app_label, lookup_kwargs)
+
+ if request.method == 'POST':
+ object.delete()
+ if request.user.is_authenticated():
+ request.user.message_set.create(message=gettext("The %(verbose_name)s was deleted.") % {"verbose_name": model._meta.verbose_name})
+ return HttpResponseRedirect(post_delete_redirect)
+ else:
+ if not template_name:
+ template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ template_object_name: object,
+ }, context_processors)
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ response = HttpResponse(t.render(c))
+ populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
+ return response
+
+ @classmethod
+ def list(cls, request, queryset, paginate_by=None, page=None,
+ allow_empty=False, template_name=None, template_loader=loader,
+ extra_context=None, context_processors=None, template_object_name='object',
+ mimetype=None):
+ """
+ Generic list of objects.
+
+ Templates: ``<app_label>/<model_name>_list.html``
+ Context:
+ object_list
+ list of objects
+ is_paginated
+ are the results paginated?
+ results_per_page
+ number of objects per page (if paginated)
+ has_next
+ is there a next page?
+ has_previous
+ is there a prev page?
+ page
+ the current page
+ next
+ the next page
+ previous
+ the previous page
+ pages
+ number of pages, total
+ hits
+ number of objects, total
+ last_on_page
+ the result number of the last of object in the
+ object_list (1-indexed)
+ first_on_page
+ the result number of the first object in the
+ object_list (1-indexed)
+ """
+ if extra_context is None: extra_context = {}
+ queryset = queryset._clone()
+ if paginate_by:
+ paginator = ObjectPaginator(queryset, paginate_by)
+ if not page:
+ page = request.GET.get('page', 1)
+ try:
+ page = int(page)
+ object_list = paginator.get_page(page - 1)
+ except (InvalidPage, ValueError):
+ if page == 1 and allow_empty:
+ object_list = []
+ else:
+ raise Http404
+ c = RequestContext(request, {
+ '%s_list' % template_object_name: object_list,
+ 'is_paginated': paginator.pages > 1,
+ 'results_per_page': paginate_by,
+ 'has_next': paginator.has_next_page(page - 1),
+ 'has_previous': paginator.has_previous_page(page - 1),
+ 'page': page,
+ 'next': page + 1,
+ 'previous': page - 1,
+ 'last_on_page': paginator.last_on_page(page - 1),
+ 'first_on_page': paginator.first_on_page(page - 1),
+ 'pages': paginator.pages,
+ 'hits' : paginator.hits,
+ }, context_processors)
+ else:
+ c = RequestContext(request, {
+ '%s_list' % template_object_name: queryset,
+ 'is_paginated': False
+ }, context_processors)
+ if not allow_empty and len(queryset) == 0:
+ raise Http404
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ if not template_name:
+ model = queryset.model
+ template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
+ t = template_loader.get_template(template_name)
+ return HttpResponse(t.render(c), mimetype=mimetype)
+
+ def detail(self, request, queryset, object_id=None, slug=None,
+ slug_field=None, template_name=None, template_name_field=None,
+ template_loader=loader, extra_context=None,
+ context_processors=None, template_object_name='object',
+ mimetype=None):
+ """
+ Generic detail of an object.
+
+ Templates: ``<app_label>/<model_name>_detail.html``
+ Context:
+ object
+ the object
+ """
+ if extra_context is None: extra_context = {}
+ model = queryset.model
+ if object_id:
+ queryset = queryset.filter(pk=object_id)
+ elif slug and slug_field:
+ queryset = queryset.filter(**{slug_field: slug})
+ else:
+ raise AttributeError, "Generic detail view must be called with either an object_id or a slug/slug_field."
+ try:
+ obj = queryset.get()
+ except ObjectDoesNotExist:
+ raise Http404, "No %s found matching the query" % (model._meta.verbose_name)
+ if not template_name:
+ template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower())
+ if template_name_field:
+ template_name_list = [getattr(obj, template_name_field), template_name]
+ t = template_loader.select_template(template_name_list)
+ else:
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ template_object_name: obj,
+ }, context_processors)
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ response = HttpResponse(t.render(c), mimetype=mimetype)
+ populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
+ return response
+
+
+# end of file
Modified: cs/portal/trunk/northridge/SeismoWebPortal/urls.py
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/urls.py 2008-05-02 02:33:07 UTC (rev 11896)
+++ cs/portal/trunk/northridge/SeismoWebPortal/urls.py 2008-05-02 03:14:00 UTC (rev 11897)
@@ -67,7 +67,7 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
urlpatterns = patterns('',
- (r'^$', 'SeismoWebPortal.views.home'),
+ (r'^$', 'SeismoWebPortal.views.root'),
(r'^login/$', 'SeismoWebPortal.views.login_view'),
(r'^logout/$', 'SeismoWebPortal.views.logout_view'),
Modified: cs/portal/trunk/northridge/SeismoWebPortal/views.py
===================================================================
--- cs/portal/trunk/northridge/SeismoWebPortal/views.py 2008-05-02 02:33:07 UTC (rev 11896)
+++ cs/portal/trunk/northridge/SeismoWebPortal/views.py 2008-05-02 03:14:00 UTC (rev 11897)
@@ -12,6 +12,7 @@
import cmt
import models
+import mezzanine
import os, os.path
from HTMLParser import HTMLParser
@@ -29,14 +30,67 @@
login_required = user_passes_test(lambda u: not u.is_anonymous(), "/specfem3dglobe/login")
-def home(request):
+def root(request):
if request.user.is_anonymous():
return render_to_response('SeismoWebPortal/splash.html', {},
RequestContext(request, {}))
- return render_to_response('SeismoWebPortal/home.html',
- {},
- RequestContext(request, {}))
+ if not request.GET:
+ return render_to_response('SeismoWebPortal/home.html',
+ {},
+ RequestContext(request, {}))
+ try:
+ className = request.GET['class']
+ except KeyError:
+ raise Http404
+
+ try:
+ Class = getattr(models, className)
+ except AttributeError:
+ raise Http404
+
+ obj = mezzanine.Object()
+ objId = request.GET.get('object')
+
+ if objId is None:
+ return obj.list(request, Class.objects.all())
+
+ try:
+ objId = int(objId)
+ except ValueError:
+ raise Http404
+
+ # NYI: mezzanine.Object instantiation belongs here.
+
+ action = request.GET.get('action')
+ if action is None:
+ return obj.detail(request, Class.objects.all(),
+ object_id = objId)
+
+ if action == 'edit':
+ return obj.update(request, Class,
+ object_id = objId)
+
+ if action == 'delete':
+ return obj.delete(request, Class, "/",
+ object_id = objId)
+
+ # Brainstorm: fixed-target action buttons/menus
+ # * create/new
+ # * update/edit/(rename)
+ # * delete
+ # * upload
+ # * search
+ # * download {text/kml/pdf/ps}
+ # * maps?
+ # --- these depend upon implementation strategy ---
+ # * save [live edit?]
+ # * undo [trash?]
+ # * help [probably css collapsable]
+
+ raise Http404
+
+
def par_file(request, sim_id):
from django.template import loader, Context
More information about the cig-commits
mailing list