Login

When you can't return a response object, throw it

Author:
motiejus
Posted:
September 6, 2011
Language:
Python
Version:
1.3
Score:
3 (after 3 ratings)

There are cases when rendering had already started, but you have to return Your response nevertheless. A good example is when you have a django-cms plugin and a form in it. You want to redirect after the form was processed, but normally you can't do it.

More information here: https://github.com/divio/django-cms/issues/79 http://groups.google.com/group/django-cms/browse_thread/thread/79ab6080c80bbcb5?pli=1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""
Motivation
==========
There are cases when rendering had already started, but you have to return Your response nevertheless. A good example is when you have a django-cms plugin and a form in it. You want to redirect after the form was processed, but normally you can't do it.

More information here:
https://github.com/divio/django-cms/issues/79
http://groups.google.com/group/django-cms/browse_thread/thread/79ab6080c80bbcb5?pli=1

Usage
=====
1) Add ForceRedirectsMiddleware to MIDDLEWARE_CLASSES in your settings.py
2) throw ForceResponse(HttpResponseRedirect(reverse('your_view_name'))) whenever you want.


Code example (working cms plugin):
==================================
from cv.utils import ForceResponse

class CVPlugin(CMSPluginBase):
    def render(self, context, instance, placeholder):
        request = context['request']
        if request.method == 'POST':
            cv_form = CVForm(request.POST, request.FILES)
        else:
            cv_form = CVForm()

        if not cv_form.is_valid():
            context.update({ 'form' : cv_form })
            return context

        # Saving CV
        cv = cv_form.save(commit=False)
        code = cv.gen_code()
        cv.save()
            
        raise ForceResponse(HttpResponseRedirect(reverse('dashboard',
            kwargs={'code': cv.code})))

plugin_pool.register_plugin(CVPlugin)
"""

class ForceResponse(Exception):
    def __init__(self, response):
        self.response = response

class ForceResponseMiddleware:
    def process_exception(self, request, e):
        """Because django plugins cannot throw raw response
        (redirect is required to user dashboard after form is submitted),
        a solution is to raise an exception, catch it with middleware and
        react.

        This middleware checks for ForceResponse exception and returns it's
        response object.

        In reality, ForceResponse is caught as TemplateSyntaxtError in cms
        plugin. So we have to extract ForceResponse from it.

        Instance of TemplateSyntaxError has exc_info field where it has the
        original exception. exc_info[1] is the exception instance.
        """
        from django.template import TemplateSyntaxError
        if isinstance(e, TemplateSyntaxError) and getattr(e, 'exc_info', 0):
            try:
                e = e.exc_info[1]
            except: # Not iterable or IndexError
                raise e # as if nothing had happened
        if isinstance(e, ForceResponse):
            return e.response

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

mludvig (on April 13, 2012):

This is exactly what I needed for my django-cms form plugin :) Thanks!

#

Please login first before commenting.