Entry 781

form view factory

   

Submitted by st_sebastian on May 16, 2008 at 12:26 p.m.
Language: Python. Code size: 4.8 KB.

def create_form_view (template_name, form_class, object_class = None, 
                      linked_class = None, allow_new = False ):
    """Create a view function as used in ``DynamicAuthedView``
    
    It will render a ModelForm instantiated from *form_class* using the template defined
    by *template_name*, and an instance of *object_class* will underlie the form.
    
    If called with a POST request, it will save the form and redirect to the created objects
    (or the linked object; see below) page by calling get_absolute url.
    
    By setting allow_new to True, the view will be able to created new instances of *model_class*.
    
    By setting *linked_class*, any new instances will get this attribute set before instantiating
    the form, allowing ForeignKey Fields to be set in advanced (And preventing the user to screw up
    the integrity of the relations"
    """
    
    obj_name = object_class and object_class.__name__.lower() or None
    linked_name = linked_class and linked_class.__name__.lower() or None
    
    def ret_view (request, **kwargs):
        """ 
        This is where we build the view function.
        We know what keyword arguments it will get from the urlhandler based in the classes
        we got from create_from view:
        
        If allow_new was True, we can expect a boolean argumet 'is_new' to the view,
        defaulting to false.
        
        object_class results in an argument named as the lowercased classname, possibly None
        (meaning is_new is True and the form should create a new instance).
        
        if linked_class was defined, this will receive the object that was used to
        authenticate the request.
        """
        if allow_new and kwargs.get('is_new', False):
            # Create a new instance of object_class here to assign to the form.            
            obj = object_class()
            if linked_class is not None:
                # if we have a linked class, then obj_class is probably a join-table
                linked_obj = kwargs.get(linked_name, None)
                if linked_obj is not None:
                    try:
                        setattr(obj, linked_name , linked_obj)
                    except:
                        print "Failed to set %s attribute on %s instance" % (linked_name, object_class)
            # as we got None as object argument, replace it with the newly created instance
            kwargs[obj_name] = obj 
        else:
            # We got one from the authenticator
            obj = kwargs.get(obj_name, None)
        form = None
        respond_options = {
            obj_name : obj
        }
        if allow_new:
            # Tell the template context this is a new instance
            respond_options['is_new'] = kwargs.get('is_new', False)        
        if request.method == "GET":
            # First request (or after invalid data submitted) - simply render the form bound 
            # to the instance
            form = form_class(instance = obj)    
            respond_options['form'] = form        
            return respond(request, template_name, **respond_options)            
        elif request.method == "POST":
            # Reached after the form was submitted
            form = form_class(instance = kwargs[obj_name], data = request.POST, files = request.FILES)            
            if form.is_valid():
                # All responsibility for handling relationships or files 
                # is given to the form, as we have little knowledge about the model here.
                form.save()
                # Check where to go from here. The form class may define this by a special
                # method ``get_redirect_url``
                if hasattr(form, "get_redirect_url"):
                    # Redirecting prevents a double-submit by the user hitting
                    # the "back" button.
                    return HttpResponseRedirect(form.get_redirect_url())
                if linked_class is not None:
                    # object_class is probably an extended join-table, so there is
                    # little use in pointing there. Instead, use the related object.
                    return HttpResponseRedirect(getattr(obj, linked_name).get_absolute_url())
                else:
                    # show the objects detail_page as a fallback
                    return HttpResponseRedirect(obj.get_absolute_url())
            else:
                # If the form was not complete (or invalid), simply render it again.
                respond_options['form'] = form
                return respond(request, template_name, **respond_options)
        else:
            return HttpResponseNotAllowed(['GET', 'POST'])
        
    return ret_view 

This snippet took 0.01 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).