app/django/views/generic/create_update.py
author Todd Larsen <tlarsen@google.com>
Sat, 04 Oct 2008 07:14:11 +0000
changeset 281 7caa8951cbc9
parent 54 03e267d67478
child 323 ff1a9aa48cfd
permissions -rw-r--r--
Url and Page classes used to encapsulate the "site map" information (URL regular expressions, Django views, menu item names, etc.) and the relation between pages in the site map. There are still lots of TODOs in this first pass. Patch by: Todd Larsen Review by: to-be-reviewed

from django.core.xheaders import populate_xheaders
from django.template import loader
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.utils.translation import ugettext

def create_object(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=ugettext("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_object(request, model, object_id=None, slug=None,
        slug_field='slug', 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=ugettext("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_object(request, model, post_delete_redirect,
        object_id=None, slug=None, slug_field='slug', 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=ugettext("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