python/django_snippets.py
author Adam Gomaa <code@adam.gomaa.us>
Mon Feb 16 09:13:56 2009 -0500
changeset 246 5ea4585bfe9b
parent 195 98fcaf41bffa
child 257 d2e837e6f0d5
permissions -rw-r--r--
Add QuerySetManager and QuerySet to my Django snippets.
     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     Prefix 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 dictproperty(method):
    49     """Turn dictionary or attribute access into a function call"""
    50 
    51     class _Object(object):
    52         def __init__(self, obj):
    53             self.obj = obj
    54         def __getattr__(self, attr):
    55             return method(self.obj, attr)
    56         __getitem__ = __getattr__
    57     _Object.__name__ = method.__name__
    58 
    59     return property(_Object)
    60 
    61 
    62 def decorators(*decos):
    63     """Some people say I need to handle my decorator problem.
    64 
    65     They are wrong.
    66 
    67     The evaulation is such that the things on the left are on the
    68     "outside" of the decorator chain. EG::
    69 
    70         @foo
    71         @bar
    72 
    73     Could also be::
    74 
    75         @decorators(foo, bar)
    76 
    77     Which itself is, of course, a shortcut for::
    78 
    79         foo(bar(function))
    80 
    81     """
    82     def _decorator(func):
    83         # The reversed is needed - step through it, it'll make sense.
    84         for deco in reversed(list(decos)):
    85             func = deco(func)
    86         return func
    87     return _decorator
    88 
    89 
    90 def cached_function(func):
    91     """A caching decorator for functions"""
    92     from functools import wraps
    93     from django.core.cache import cache
    94 
    95     @wraps(func)
    96     def _closure(*args, **kwargs):
    97         cache_key = cache_key_for_function(
    98             func, args, kwargs)
    99 
   100         val = cache.get(cache_key)
   101         if val is None:
   102             val = func(*args, **kwargs)
   103             cache.set(cache_key, val)
   104         return val
   105     return _closure
   106 
   107 
   108 def cached_method(method):
   109     """A caching decorator for methods"""
   110     from functools import wraps
   111     from django.core.cache import cache
   112 
   113     @wraps(method)
   114     def _closure(self, *args, **kwargs):
   115         cache_key = cache_key_for_method(
   116             self, method, (self,)+args, kwargs)
   117 
   118         val = cache.get(cache_key)
   119         if val is None:
   120             val = method(self, *args, **kwargs)
   121             cache.set(cache_key, val)
   122         return val
   123     return _closure
   124 
   125 cached_property = decorators(property, cached_method)
   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 class QuerySet(models.query.QuerySet):
   176     """Base QuerySet class for adding custom methods that are made
   177     available on both the manager and subsequent cloned QuerySets"""
   178 
   179     @classmethod
   180     def as_manager(cls, ManagerClass=QuerySetManager):
   181         return ManagerClass(cls)
   182 
   183     def random(self):
   184         return self.order_by("?")
   185 
   186     def first(self):
   187         return self[0]