# -*- coding: utf-8 -*- from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.template import defaulttags, RequestContext, TemplateSyntaxError, Node, Context from django.template.loader import get_template, select_template try: from django.utils import simplejson from django.core.serializers.json import DjangoJSONEncoder except ImportError: pass # does'nt work in 0.97 import types class ObjectCaller: def __init__(self, klass, *args, **kwargs): self.klass = klass self.args = args self.kwargs = kwargs def __call__(self, *args, **kwargs): obj = self.klass(*(self.args), **(self.kwargs)) return obj(*args, **kwargs) class MetaViewClass(type): def __new__(meta, classname, bases, classDict): if classname != 'BaseView': classDict['_context_attrs'] = {} for base in bases: if base.__dict__.has_key('_context_attrs'): classDict['_context_attrs'].update(base._context_attrs) for attname, value in classDict.items(): if not attname.startswith('_') and not type(value) in (types.ClassType, types.FunctionType): classDict['_context_attrs'][attname] = attname return type.__new__(meta, classname, bases, classDict) class BaseView(object): __metaclass__ = MetaViewClass template = '' request = None context = None def __init__(self, *args, **kwargs): self.context = {} self.template = kwargs.get('template') or self.template self.context.update(kwargs.get('extra_context', {})) def get_template(self): return self.template def render_html(self): return render_to_response(self.get_template(), self.context, context_instance=RequestContext(self.request)) def render_json(self, to_pack=None): response = HttpResponse(mimetype='application/x-javascript') json = to_pack is None and self.context or to_pack if json == self.context: del self.context['self'] simplejson.dump(json, response, ensure_ascii=False, cls=DjangoJSONEncoder ) return response def render(self): return self.render_html() def fill_extra_context(self): # DEPRECATED pass def fill_context(self): """Defined in subclasses""" pass def _fill_context_with_attrs(self): for attname in self._context_attrs.keys(): if not self.context.has_key(attname): self.context[attname] = getattr(self, attname) def process(self): response = self.fill_context() if hasattr(response, 'status_code'): # http response return response response = self.fill_extra_context() if hasattr(response, 'status_code'): # http response return response self._fill_context_with_attrs() return self.render() def set_initial_attrs(self, request, kwargs): self.request = request self.context['self'] = self for var, value in kwargs.items(): setattr(self, var, value) def __call__(self, request, *args, **kwargs): self.set_initial_attrs(request, kwargs) return self.process() __compiled_template__ = None def inclusion_tag(self, file_name, extra_context=None): if self.__compiled_template__ is None: self.__compiled_template__ = {} nodelist = self.__compiled_template__.get(file_name) if nodelist is None: if not isinstance(file_name, basestring) and getattr(file_name, '__iter__', False): t = select_template(file_name) else: t = get_template(file_name) nodelist = self.__compiled_template__[file_name] = t.nodelist if extra_context: context = self.context.copy() context.update(extra_context) else: context = self.context return nodelist.render(Context(context)) def redirect_self(self): return HttpResponseRedirect(self.request.get_full_path()) # Thanks to Todd Reed from http://www.toddreed.name/content/django-view-class/ def on_method(function_decorator, *deco_args, **deco_kwargs): def decorate_method(unbound_method): def method_proxy(self, *args, **kwargs): def f(*a, **kw): return unbound_method(self, *a, **kw) return function_decorator(f, *deco_args, **deco_kwargs)(*args, **kwargs) return method_proxy return decorate_method # inspired by http://code.djangoproject.com/wiki/CallTag class CallNode(Node): def __init__(self, method_name, *args, **kwargs): self.method_name = method_name self.args = args self.kwargs = kwargs def render(self, context): method_name = self.method_name.resolve(context) method = getattr(context['self'], method_name) d = {} args = d['args'] = [] args = [ arg.resolve(context) for arg in self.args ] return method(*args) def do_call(parser, token): bits = token.contents.split() if len(bits) < 2: raise TemplateSyntaxError, "%r tag takes at least one argument" % bits[0] method_name = parser.compile_filter(bits[1]) if method_name.token[1] == '_': raise TemplateSyntaxError, "Private method calls are not allowed: %r" % method_name.token args = [ parser.compile_filter(arg) for arg in bits[2:] ] return CallNode(method_name, *args) defaulttags.register.tag('call', do_call) # Perhaps the line above is not very academic