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