Login

View and StatefulView classes

Author:
Digitalxero
Posted:
October 13, 2008
Language:
Python
Version:
1.0
Score:
0 (after 2 ratings)

This snippet provides two view classes. The two reason I wanted to write this are, 1) Not have to import all the boilerplate code for each view and 2) so I could have the same URL handle loading a persons profile, or handle an OpenID login request without having to write two separate views. (Yes I know it isnt to hard to write my view, check the header and pass it off to the proper handler view, but I think it looks better code wise to have the handler methods all in one class)

The first one is just for normal views conveniently called View. The View class that lets you do at least 90% of what you can do in a normal view function, but without having to import all the normal boilerplate code first since this class wraps methods around most if not all the HttpResponse types.

The second class StatefulView maintains its state across page loads This is especialy useful for ajax type calls where you wish to maintain some form of state while the user is doing something but do not wish to make DB calls and do not wish to polute the session with trivial things

Note: On my system it maintains state across browsers and computers as it is not tied to the session, BUT for this to happen all requests must be handled by the same proccess. So requests going to a differing process with not have the state maintained.

  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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
try:
    frozenset
except NameError:
    # Import from the sets module for python 2.3
    from sets import Set as set
    from sets import ImmutableSet as frozenset

from datetime import datetime, timedelta
from threading import Thread
import time

from django.shortcuts import render_to_response
from django.http import (
Http404, HttpResponse, HttpResponseRedirect,
HttpResponsePermanentRedirect, HttpResponseGone, HttpResponseNotAllowed,
HttpResponseForbidden, HttpResponseBadRequest, HttpResponseNotModified,
HttpResponseServerError
)
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.conf import settings

class View(object):
    """
        View class that lets you do at least 90% of what you can do in a normal
        view function, but without having to import all the normal boilerplate
        code first since this class wraps methods around most if not all the
        HttpResponse types
    """
    context = {}
    template = None
    allowed_methods = frozenset(['GET', 'POST', 'HEAD', 'OPTIONS', 'PUT',
        'DELETE', 'TRACE', 'CONNECT'])
    build_response = False
    mimetype = None
    
    def __call__(self, request, *args, **kwargs):
        """
            This is the meat of the class, it lets it act as a callable
            Thus allowing the url handler to call the correct view
            
            Whats happening is it first sets the class request attribute,
            so it can be accessed via the other class methods
            Second it checks to make sure the requests method (GET, POST, etc)
            is in the allowed types list, if it is not then retuns a not allowed
            page
            Next it check to see if you want to build your own response (False by default), 
            thus setting self.response to an empty HttpResponse that you can
            use to manualy build up your response. If you just want the normal
            way of using render_to_response it sets self.response to self.http_response
            
            Finnaly it attempts to call render without sending it the request object
            Since render will have access to the request object via self.request
            this should be the normal way of sending it, BUT if you need to 
            decorate render with somnething like @login_required, then it needs
            to pass the request object along with it so it can be used by the
            decorator
        """
        self.request = request
        
        if self.request.method not in self.allowed_methods:
            return self.not_allowed(self.allowed_methods)
            
        if self.build_response:
            self.response = HttpResponse()
        else:
            self.response = self.http_response
        
        try:
            return self.render(*args, **kwargs)
        except TypeError:
            return self.render(request, *args, **kwargs)
        
    def render(self, *args, **kwargs):
        """
            This is the method that gets called when your view gets called
            So this is the method you need to override in your subclass
        """
        return HttpResponse("This View is not yet Implimented!")
        
    def http_response(self):
        """
            Wrapper around the render_to_response shortcut. It uses the classes
            self.template, self.context, and self.mimetype and ensures that you 
            get a normal request_context
        """
        return render_to_response(self.template, self.context,
            context_instance=RequestContext(self.request), mimetype=self.mimetype)
    
    def raw_response(self, msg, **kwargs):
        """
            Wrapper around the HttpResponse object so you can deal with a raw
            response instead of being forced to use the render_to_response            
        """
        return HttpResponse(msg, **kwargs)
        
    def redirect(self, url, reverse=True):
        """
            Wrapper around the HttpResponseRedirect class. Takes a url, and assumes
            that you want to use reverse to find the actual url
        """
        if reverse:
            url = reverse(url)
        return HttpResponseRedirect(url)    
    
    def permanent_redirect(self, url, reverse=True):
        """
            Wrapper around the HttpResponsePermanentRedirect class. Takes a url, and assumes
            that you want to use reverse to find the actual url
        """
        if reverse:
            url = reverse(url)
        return HttpResponsePermanentRedirect(url)   
    
    def gone(self):
        """
            Wrapper around the HttpResponseGone class.
        """
        return HttpResponseGone()
        
    def not_allowed(self, methods):
        """
            Wrapper around the HttpResponseNotAllowed class.
        """
        return HttpResponseNotAllowed(methods)
        
    def forbidden(self):
        """
            Wrapper around the HttpResponseForbidden class.
        """
        return HttpResponseForbidden()
        
    def server_error(self):
        """
            Wrapper around the HttpResponseServerError class.
        """
        return self.Http500()
    
    def not_found(self):
        """
            Wrapper around the HttpResponseNotFound class.
        """
        self.Http404()    
    
    def bad(self):
        """
            Wrapper around the HttpResponseBadRequest class.
        """
        return HttpResponseBadRequest()
        
    def unmodified(self):
        """
            Wrapper around the HttpResponseNotModified class.
        """
        return HttpResponseNotModified()
    
    def Http404(self):
        """
            Raises the 404 exception
        """
        raise Http404
    
    def Http500(self):
        """
            Wrapper around the HttpResponseServerError class.
        """
        return HttpResponseServerError()
        
    def is_ajax(self):
        """
            Wrapper around the request.is_ajax method
            Should work on 0.96 as well if anyone is still using that
        """        
        try:
            return self.request.is_ajax()
        except:
            idx = self.request.META['HTTP_X_REQUESTED_WITH'].find('XMLHttpRequest')
            if idx > -1:
                return True
        
        return False
            
    def is_secure(self):
        """
            Wrapper around the request.is_secure method
        """        
        return self.request.is_secure()
        
        
class StatefulView(View):
    """
        This is a StatefulView class that maintains its state across page loads
        This is especialy useful for ajax type calls where you wish to maintain
        some form of state while the use is doing something but do not wish to
        make DB calls and do not wish to polute the session with trivial things
        
        NOTE: On my system it maintains state across browsers and computers as it is
        not tied to the session, BUT for this to happen all requests must be handled
        by the same proccess. So requests going to a differing process with not have
        the state maintained.
    """
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass

"""
from apps.utils.views import View, StatefulView

class Home(View):
    template = 'index.html'
    
    def render(self):
        return self.response()
        
class Test(StatefulView):
    counter = 0
    build_response = True
    
    def render(self):
        self.counter += 1
        self.response.write("You have viewed this page %d times" % (self.counter,))
        return self.response

index = Home()
test = Test()
"""

More like this

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

Comments

Please login first before commenting.