Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from django.views.generic.base import View, TemplateResponseMixin, TemplateView
- from django.template import RequestContext
- import csv
- from django.http import *
- from django.core.exceptions import PermissionDenied, ImproperlyConfigured
- from django.utils.decorators import method_decorator
- # Jacob Conrad Martin
- # http://jacobconradmartin.com
- # ---------------------------------------------
- # SIMPLEST POSSIBLE USAGE
- # ---------------------------------------------
- class Home(TemplateView):
- """
- This uses the bog-standard TemplateView class.
- This class provides a really simple way to display a template with some context.
- """
- template_name = 'home.html'
- def get_context_data(request):
- context = {}
- return context
- # ---------------------------------------------
- # EXPERIMENT ONE: MORE REALISTIC EXAMPLE
- # ---------------------------------------------
- class MultipleResponseBaseView(TemplateResponseMixin, View):
- """
- This is a minimal example of a view which can be inherited from to provide
- separated responses to HTTP GET and POST (etc.) requests. Derived classes
- should specify get(), post() etc. methods in order to handle them.
- The TemplateResponseMixin class provides a render_to_response() method which
- renders the given template_name within the provided context, i.e. you
- automatically get the old RequestContext functionality without having
- to type it out every time.
- The View class provides the as_view() method required in urls.py and also
- the machinery to ensure that HTTP requests are dispatched to get(), post(),
- and so on.
- For further insights, read the Django source code for these classes here:
- https://code.djangoproject.com/browser/django/tags/releases/1.3/django/views/generic/base.py
- """
- template_name = None
- def render(self, context={}):
- """
- This wrapper function allows a shorthand way to render a response.
- """
- return self.render_to_response(context)
- class ExperimentOne(MultipleResponseBaseView):
- """
- Demonstrates how to derive from the MultipleResponseBaseClass in order to
- handle GET and POST requests. Other types of request will fail via the
- http_method_not_allowed() method in the View grandparent class.
- """
- template_name = 'experiment_one.html'
- def get(self, request):
- return self.render()
- def post(self, request):
- person_name = request.POST.get('person_name', None)
- return self.render(locals())
- # ---------------------------------------------
- # EXPERIMENT TWO: AUTOMAGICALLY DOING CSV OUTPUT
- # ---------------------------------------------
- class CSVResponseMixin(object):
- """
- Provides a render_to_csv() method which will render out the context to CSV.
- We expect that the supplied context consists of:
- - filename (a string)
- - headings (a list of strings)
- - rows (a list of lists of strings)
- """
- def render_to_csv(self, context):
- # Get the data to be rendered as CSV.
- try:
- csv_context = context['csv']
- except:
- raise ImproperlyConfigured('The supplied context does not contain an key called "csv".')
- # Get the filename, headings and rows to use
- try:
- filename = csv_context['filename']
- headings = csv_context['headings']
- rows = csv_context['rows']
- except:
- raise ImproperlyConfigured('You must specify "filename", "headings" and "rows" within the "csv" dict.')
- # Initial setup
- charset = 'windows-1252'
- response = HttpResponse(mimetype='text/csv; charset=%s' % charset)
- response['Content-Disposition'] = 'attachment; filename=%s' % filename
- w = csv.writer(response)
- # Headings
- headings = [unicode(c).encode(charset, 'replace') for c in headings]
- w.writerow(headings)
- # Rows
- for row in rows:
- row = [unicode(c).encode(charset, 'replace') for c in row]
- w.writerow(row)
- # Done
- return response
- class ExperimentTwo(MultipleResponseBaseView, CSVResponseMixin):
- """
- Demonstrates how to derive from the MultipleResponseBaseClass in order to
- handle GET and POST requests. Other types of request will fail via the
- http_method_not_allowed() method in the View grandparent class.
- """
- template_name = 'experiment_two.html'
- def get(self, request):
- return self.render()
- def post(self, request):
- csv = {
- 'filename': 'awesome.csv',
- 'headings': ['foo', 'bar', 'baz'],
- 'rows': [ [1,2,4], [2,4,6], [4,8,16] ]
- }
- context = { 'csv': csv }
- return self.render_to_csv(context)
- # ---------------------------------------------
- # EXPERIMENT THREE: AUTHENTICATION MIXIN
- # ---------------------------------------------
- class SuperuserRequiredMixin(object):
- """
- Demonstrates how you go about packing all your authentication into a mixin.
- Here we just test that the user is logged in and is a superuser. Derived
- classes must ensure that they always call the get() method defined below,
- i.e. by using super().
- FIXME: Is there some way to make this happen automatically?
- """
- def get(self, request):
- if not request.user.is_superuser:
- raise PermissionDenied
- class ExperimentThree(MultipleResponseBaseView, SuperuserRequiredMixin):
- """
- Example of how to use the authentication mixin above.
- Note that classes deriving from SuperuserRequiredMixin must always
- ensure that they call the superclass method or it will not execute.
- """
- template_name = 'experiment_three.html'
- def get(self, request):
- # Ensure that we have call the get() method of the superclass
- super(ExperimentThree, self).get(request)
- # If the superclass didn't raise PermissionDenied then the following code will execute
- return self.render()
- # ---------------------------------------------
- # EXPERIMENT FOUR: FUNCTIONALITY MIXIN (A.K.A. BOILERPLATE REDUCTION MIXIN)
- # ---------------------------------------------
- class FooObjectRequiredMixin(object):
- """
- Demonstrates how you would go about performing generic get_object_or_404
- type of behaviour in a transparent way for lots of different but similar views.
- You can use this technique to perform all sorts of boilerplate activities
- so that your derived classes (i.e. views which map to URLs) can be as
- simple as possible.
- """
- def get_foo_or_404(self, foo_id):
- # We're not hooking this up to a database so instead we'll just pretend
- # that odd-numbered IDs exist and even-numbered IDs do not exist.
- if int(foo_id) % 2 == 0:
- return 'Foo Object ' + foo_id
- else:
- raise Http404
- class ExperimentFour(MultipleResponseBaseView, FooObjectRequiredMixin):
- """
- Example of how to use the functionality mixin above.
- """
- template_name = 'experiment_four.html'
- def get(self, request, foo_id=None):
- # Show the homepage for the experiment if a value for foo_id was not specified
- if foo_id == None:
- return self.render()
- # The user has selected a foo_id, so let's try and fetch the appropriate object
- # (If the user requests an odd-numbered foo_id they will get a 404 error by design)
- foo = self.get_foo_or_404(foo_id)
- # Render the template
- return self.render(locals())
- # ---------------------------------------------
- # EXPERIMENT FIVE: IMPLICIT AUTHENTICATION MIXIN
- # ---------------------------------------------
- class ViewWithRenderShortCut(View):
- template_name = None
- def render(self, context={}):
- """
- This wrapper function allows a shorthand way to render a response.
- """
- return self.render_to_response(context)
- class ImplicitSuperUserRequiredView(ViewWithRenderShortCut):
- """
- Requires a superuser for all HTTP request verb.
- This could be easily customised to do something different for each verb.
- Or even to do things like fetch an object if given its ID (or return a 404).
- """
- def dispatch(self, request, *args, **kwargs):
- """
- Hijack the dispatch() method of the View class to ensure that the user is a superuser
- before dispatching the HTTP request off to a method matching the HTTP verb.
- """
- if not request.user.is_superuser:
- # Django can't do "raise Http 403" just yet.
- raise PermissionDenied
- else:
- # This is just a demo of creating a variable within a scope available to derived classes.
- # In real apps, you'd probably want to do existence checks on objects here, e.g. if the
- # request signature contained a reference to a supplier_id then you could fetch the
- # supplier object into score or raise a 404 if it could not be located. This would avoid
- # boilerplate code being used everywhere.
- self.lucky_number = '42'
- return super(ImplicitSuperUserRequiredView, self).dispatch(request, *args, **kwargs)
- class ExperimentFive(TemplateResponseMixin, ImplicitSuperUserRequiredView):
- """
- Example of how to use the implicitly applied authentication mixin above.
- """
- template_name = 'experiment_five.html'
- def get(self, request):
- # The dispatch method has already been hijacked, so the next
- # line should only run if the user is a superuser.
- lucky_number = self.lucky_number
- return self.render(locals())
- # ---------------------------------------------
- # IMPLICIT FUNCTIONALITY MIXIN (A.K.A. BOILERPLATE REDUCTION MIXIN)
- # ---------------------------------------------
- class ImplicitObjectExistenceEnforcerView(ViewWithRenderShortCut):
- """
- Example of how to extend the concept of implicitly applied authentication to
- achieve implicitly extended functionality, specifically to automatically
- ensure the existence of objects referred to in the method signatures of
- derived classes.
- NOTE: This required the ViewWithRenderShortcut defined above.
- """
- def dispatch(self, request, *args, **kwargs):
- """
- The general strategy here is to look for known (agreed) kwargs and try
- to fetch the relevant objects into scope. If they do not exist then a
- Http404 error is returned instead. Note that we could generalise this
- approach to handle any value supplied in kwargs, but IMHO it is better
- to be explicit about which objects you provide this functionality for.
- """
- # Was a foo_id supplied?
- self.foo = None
- if 'foo_id' in kwargs:
- # If so, ensure it exists. (Note: We'd do a DB lookup in the real world)
- foo_id = int(kwargs['foo_id'])
- if foo_id == 2: self.foo = 'I am foo #2'
- else: raise Http404
- # Was a bar_id supplied?
- self.bar = None
- if 'bar_id' in kwargs:
- # If so, ensure it exists. (Note: We'd do a DB lookup in the real world)
- foo_id = int(kwargs['bar_id'])
- if foo_id == 2: self.bar = 'I am bar #2'
- else: raise Http404
- return super(ImplicitObjectExistenceEnforcerView, self).dispatch(request, *args, **kwargs)
- class ExperimentSix(TemplateResponseMixin, ImplicitObjectExistenceEnforcerView):
- """
- This view can be incredibly simple because the machinery to check for object
- existence (and act accordingly if the objects do not exist) is neatly tucked
- away in the superclass.
- """
- template_name = 'experiment_six.html'
- def get(self, request, foo_id=None, bar_id=None):
- """
- Note that by specifying foo_id and bar_id in the method signature above we can be assured that
- there will be a self.foo object and a self.bar object for the remainder of our code, because the
- ImplicitObjectExistenceEnforcerView view contains the relevant machinery to make this happen or
- raise a Http404 accordingly.
- """
- foo = self.foo
- bar = self.bar
- return self.render(locals())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement