decorators.py 3.59 KB
# -*- coding: utf-8 -*-

from functools import wraps
from inspect import getargspec
from django.http import HttpResponse
from django.utils.simplejson import loads as json_decode, dumps as json_encode
from django.utils.functional import curry
from django.conf import settings
from django.shortcuts import render_to_response
from django.template import RequestContext, generic_tag_compiler, Node
from django.template import TemplateSyntaxError, Variable
from django.db import transaction
from common.util import stringify_keys

class AjaxError(Exception):
  pass

def json_decode_fallback(value):
  try:
    return json_decode(value)
  except ValueError:
    return value

def ajax(login_required=True, method=None, encode_result=True):
  def decorator(fun):
    @wraps(fun)
    def ajax_view(request):
      kwargs = {}
      request_params = None
      if method == 'post':
        request_params = request.POST
      elif method == 'get':
        request_params = request.GET
      fun_params, xx, fun_kwargs, xxxx = getargspec(fun)
      if request_params:
        request_params = dict((key, json_decode_fallback(value))
          for key, value in request_params.iteritems()
          if fun_kwargs or key in fun_params)
        kwargs.update(stringify_keys(request_params))
      res = None
      if login_required and not request.user.is_authenticated():
        res = {'result': 'logout'}
      if not res:
        try:
          res = fun(request, **kwargs)
        except AjaxError as e:
          transaction.rollback()
          res = {'result': e.args[0]}
      if encode_result:
        if 'result' not in res:
          res['result'] = 'ok'
        return HttpResponse(json_encode(res), mimetype='application/json')
      else:
        return res
    return ajax_view
  return decorator

def render(template=None, mimetype=None):
  mimetype = mimetype or settings.DEFAULT_CONTENT_TYPE
  template1 = template
  def decorator(func):
    template = template1 # no cóż...
    if not template:
      template = func.__name__ + '.html'
    @wraps(func)
    def renderer(request, *args, **kw):
      output = func(request, *args, **kw)
      if isinstance(output, (list, tuple)):
        return render_to_response(output[1], output[0],
                                  RequestContext(request), mimetype=mimetype)
      elif isinstance(output, dict):
        return render_to_response(template, output,
                                  RequestContext(request), mimetype=mimetype)
      return output
    return renderer
  return decorator

def simple_tag(register, takes_context=False):
  def decorator(func):
    params, xx, xxx, defaults = getargspec(func)
    if takes_context:
      if params[0] == 'context':
        params = params[1:]
      else:
        raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")

    class SimpleNode(Node):
      def __init__(self, vars_to_resolve):
        self.vars_to_resolve = map(Variable, vars_to_resolve)

      def render(self, context):
        resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
        if takes_context:
          args = [context] + resolved_vars
        else:
          args = resolved_vars
        try:
          return func(*args)
        except Exception as e:
          raise
          return type(e).__name__+': '+str(e)

    compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
    compile_func.__doc__ = func.__doc__
    register.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
    return func
  return decorator