Login

Django Class Views

Author:
rmt
Posted:
June 22, 2009
Language:
Python
Version:
1.0
Tags:
django view class
Score:
4 (after 4 ratings)

After using Zope3/Grok for a little, I wondered how hard it would be to implement views as classes in Django, in a similar vain to how it's done in Grok. I came up with something rather simple but effective. It may be more appropriate if you use a template engine other than Django Templates, which allows you to call functions with arguments, but it's still useful none-the-less to encapsulate functions in a class.

You could, for example, extend View to be JinjaView, just replacing render_template().

A nice extension, I imagine, would be to automatically figure out the template name as well as the path prefix for it (since you probably want it to be found under packagename/templatename.html).

  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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from django.template.loader import get_template
from django.template import RequestContext
from django import http

class BaseView(object):
    """
    BaseView provides a class that is instantiated and then
    called like a function.

    __init__ called without arguments.
    You must just override the __call__ method.
    """
    def __new__(cls, *args, **kwargs):
        obj = super(BaseView, cls).__new__(cls)
        return obj(*args, **kwargs)
    
    def __call__(self, *args, **kwargs):
        pass

class View(BaseView):
    """
    View provides a basic django view _class_

    It is instantiated for each request, and it is provided to
    the template's context as the 'view' varable.

    __call__ sets the following:
        self.request
        self.response
        self.context = {}

    view.update() is called first.  It may do a redirect, additional
    checks, or setup information for the template renderer.

    If view.render() is defined, it will be run to generate the output.
    If its return value is a string (or unicode), self.response.content
    will be set to this value.  Otherwise its return value will be
    returned directly, possibly allowing other Django processors to do
    their magic.

    If view.render() is not defined, we assume it will be rendered
    with a template, so we define a context dictionary, then call
    self.render_template.

    The context dictionary contains:
        'view' : self,
        'request' : self.request,
        'response' : self.response,
    plus anything defined in self.context is merged into the dictionary,
    possibly overriding the above variables.

    Override render_template(template_name, context_dictionary) to
    use an alternate template rendering mechanism.
    """
    template = None
    context = None

    def redirect(self, url):
        self.response = http.HttpResponseRedirect(url)

    def update(self):
        pass

    def __call__(self, request, *args, **kwargs):
        self.request = request
        self.response = http.HttpResponse()
        self.update()
        if self.response.status_code in (301, 302):
            return self.response
        if hasattr(self, "render"):
            res = self.render()
            if isinstance(res, basestring):
                self.response.content = res
                return self.response
            return res
        elif self.template:
            template = self.template
            context = {
                'view' : self,
                'request' : self.request,
                'response' : self.response,
            }
            extra = getattr(self, 'context', None)
            if extra:
                context.extend(extra)
            r = self.response
            r.content = self.render_template(template, context)
            return r
        else:
            if not self.response.content:
                self.response.content = 'No Template nor Render Method'
            return self.response

    def render_template(self, template, context):
        """
        Given a template (either a filename to lookup, or an object
        with a render() method), lookup the template if necessary, then
        wrap the context in a RequestContext and call
        template.render(context)
        """
        if isinstance(template, basestring):
            template = get_template(template)
        return template.render(RequestContext(self.request, context))


### example view
class testview(View):
    template = "testview.html"

    def update(self):
        self.age = 29
        self.context["nose"] = "normal sized"
        self.response["X-Crazy"] = "Maybe"
        if self.request.GET.get('next'):
            self.redirect(self.request.GET['next'])

    def name(self):
        return "Robert"

class testview2(View):
   template = "testview.html"
   def render(self):
      return "No template will be used here automatically"

### testview.html:
Hello {{ view.name }}!  You are {{ view.age }} years old.
You have a {{ nose }} nose.

More like this

  1. Caching XHTML render_to_response by smoonen 6 years, 7 months ago
  2. Complex Form Preview by smagala 5 years, 11 months ago
  3. Controller Class for Views by jovialbard 1 year, 10 months ago
  4. FieldsetForm by Ciantic 7 years, 10 months ago
  5. Convenient class based views by eallik 4 years, 9 months ago

Comments

Please login first before commenting.