python/django_snippets.py
author Adam Gomaa <adam@gomaa.us>
Sun Dec 18 13:13:02 2011 -0500
changeset 521 67f37d330ad0
parent 246 5ea4585bfe9b
permissions -rw-r--r--
Remove project & _list_projects, I don't use them anymore.
     1 """
     2 Random Django snippets I like to keep for quick reference
     3 
     4 """
     5 
     6 def cache_key_for_function(func, args=None, kwargs=None):
     7     """Calculate a cache key for a function
     8 
     9     Prefixes keys with the module and function name.
    10 
    11     Use sha1 of repr() of args and kwargs as the final piece of the
    12     cache key. This limits the maximum length (so we don't go over
    13     memcached's limit, which I don't remember offhand) and swallows
    14     control characters.
    15 
    16     This relies on accurate reprs - if the repr of the arguments are
    17     the same, the hash will be the same, and the cached value will be
    18     returned.
    19 
    20     """
    21     from hashlib import sha1
    22 
    23     if args is None:
    24         args = ()
    25     if kwargs is None:
    26         kwargs = {}
    27 
    28     cache_key = "%s.%s(%s)" % (
    29         func.__module__, func.__name__,
    30         sha1(repr(args)+repr(kwargs)).hexdigest())
    31 
    32     return cache_key
    33 
    34 
    35 def cache_key_for_method(obj, method, args=None, kwargs=None):
    36     "Like cache_key_for_function, but for methods"
    37     from hashlib import sha1
    38 
    39     cache_key = "%s.%s.%s(%s)" % (
    40         obj.__class__.__module__,
    41         obj.__class__.__name__,
    42         method.__name__,
    43         sha1(repr(args)+repr(kwargs)).hexdigest())
    44 
    45     return cache_key
    46 
    47 
    48 def cached_function(func):
    49     """A caching decorator for functions"""
    50     from functools import wraps
    51     from django.core.cache import cache
    52 
    53     @wraps(func)
    54     def _closure(*args, **kwargs):
    55         cache_key = cache_key_for_function(
    56             func, args, kwargs)
    57 
    58         val = cache.get(cache_key)
    59         if val is None:
    60             val = func(*args, **kwargs)
    61             cache.set(cache_key, val)
    62         return val
    63     return _closure
    64 
    65 
    66 def cached_method(method):
    67     """A caching decorator for methods"""
    68     from functools import wraps
    69     from django.core.cache import cache
    70 
    71     @wraps(method)
    72     def _closure(self, *args, **kwargs):
    73         cache_key = cache_key_for_method(
    74             self, method, (self,)+args, kwargs)
    75 
    76         val = cache.get(cache_key)
    77         if val is None:
    78             val = method(self, *args, **kwargs)
    79             cache.set(cache_key, val)
    80         return val
    81     return _closure
    82 
    83 cached_property = decorators(property, cached_method)
    84 
    85 
    86 def dictproperty(method):
    87     """Turn dictionary or attribute access into a function call"""
    88 
    89     class _Object(object):
    90         def __init__(self, obj):
    91             self.obj = obj
    92         def __getattr__(self, attr):
    93             return method(self.obj, attr)
    94         __getitem__ = __getattr__
    95     _Object.__name__ = method.__name__
    96 
    97     return property(_Object)
    98 
    99 
   100 def decorators(*decos):
   101     """Some people say I need to handle my decorator problem.
   102 
   103     They are wrong.
   104 
   105     The evaulation is such that the things on the left are on the
   106     "outside" of the decorator chain. EG::
   107 
   108         @foo
   109         @bar
   110 
   111     Could also be::
   112 
   113         @decorators(foo, bar)
   114 
   115     Which itself is, of course, a shortcut for::
   116 
   117         foo(bar(function))
   118 
   119     """
   120     def _decorator(func):
   121         # The reversed is needed - step through it, it'll make sense.
   122         for deco in reversed(list(decos)):
   123             func = deco(func)
   124         return func
   125     return _decorator
   126 
   127 
   128 def use_template(template_name, request_context=True):
   129     """A view decorator that wraps render_to_response and uses RequestContext by default"""
   130     from functools import wraps
   131     from django.template import RequestContext
   132     from django.shortcuts import render_to_response
   133 
   134     def _decorator(func):
   135         @wraps(func)
   136         def _closure(request, *args, **kwargs):
   137             # Use a different variable to avoid 'referenced before
   138             # assignment'. 'template' is the kwarg used by
   139             # direct_to_template; copy its interface for consistency.
   140             actual_template = kwargs.pop("template", template_name)
   141             actual_rc = kwargs.pop("request_context", request_context)
   142             val = func(request, *args, **kwargs)
   143             if isinstance(val, dict) and template_name is not None:
   144                 if actual_rc:
   145                     context = RequestContext(request)
   146                     return render_to_response(actual_template, val,
   147                                               context_instance=context)
   148                 else:
   149                     return render_to_response(actual_template, val)
   150             return val
   151         return _closure
   152     return _decorator
   153 
   154 
   155 # http://www.djangosnippets.org/snippets/562/#c673
   156 class QuerySetManager(models.Manager):
   157     # http://docs.djangoproject.com/en/dev/topics/db/managers/#using-managers-for-related-object-access
   158     # Not working cause of:
   159     # http://code.djangoproject.com/ticket/9643
   160     use_for_related_fields = True
   161     def __init__(self, qs_class=models.query.QuerySet):
   162         self.queryset_class = qs_class
   163         super(QuerySetManager, self).__init__()
   164 
   165     def get_query_set(self):
   166         return self.queryset_class(self.model)
   167 
   168     def __getattr__(self, attr, *args):
   169         try:
   170             return getattr(self.__class__, attr, *args)
   171         except AttributeError:
   172             return getattr(self.get_query_set(), attr, *args)
   173 
   174 
   175 # http://adam.gomaa.us/blog/2009/feb/16/subclassing-django-querysets/
   176 class QuerySet(models.query.QuerySet):
   177     """Base QuerySet class for adding custom methods that are made
   178     available on both the manager and subsequent cloned QuerySets"""
   179 
   180     @classmethod
   181     def as_manager(cls, ManagerClass=QuerySetManager):
   182         return ManagerClass(cls)
   183 
   184     def random(self):
   185         return self.order_by("?")
   186 
   187     def first(self):
   188         return self[0]