Django项目中的缓存

1、Django缓存

缓存的作用自不必多说,在Django中也少不了缓存的使用,主要用在数据库以及页面的缓存。

1)、缓存设置

CACHES = 
'memcache': {
    'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
    'LOCATION': 'unix:/home/the5fire/memcached.sock',#通过sock文件进行交互
                 #‘127.0.0.1:11211’,
                 #‘127.0.0.1:11233’,   可以运行多个Memcached
    'options': {
        'MAX_ENTRIES': 1024,#高速缓存允许的最大条目数,超出这个数则旧值将被删除. 这个参数默认是300.

    }

Django中缓存的设置可以设置多种类型,在Django官方文档中有详细的介绍,但主要使用Memcached。

2)、缓存的基本使用

首先说一个使用缓存的例子。在该例子中,将每一次访问的用户的IP保存在缓存中。源代码如下:

class ArticleDetailView(BaseMixin,DetailView):

    def get(self, request, *args, **kwargs):
        alias = self.kwargs.get('slug')
        try:
            self.object = self.queryset.get(alias=alias)
        except Article.DoesNotExist:
            logger.error('article does not exsists')
            #I should rewrite the 404 html later
            return HttpResponseNotFound('<h1>Page not Found</h1>')
        # add permission,if the article has set permission,the web will raise one exveption
        if not self.object.can_access(request.user):
            raise PermissionDenied
        #Here,it should be set cache,which store the visited IP
        Article.objects.filter(id=self.object.id).update(read_times=F('read_times')+1)
        ip = get_real_ip(request)
        visited_ips = cache.get(alias,[])
        if ip not in visited_ips:
            visited_ips.append(ip)
            cache.set(alias,visited_ips,60*15)
        context = self.get_context_data(article=self.object)
        context['visited_ips'] = len(visited_ips)
        return self.render_to_response(context)

3)Django缓存的种类

1、数据库缓存

将缓存存储到数据库中,在官方文档也说了,如果你有一台性能良好的数据库服务器,可以采用这种方法,否则还是别用了。

"""
把BACKEND设置为django.core.cache.backends.db.DatabaseCache
把 LOCATION 设置为 tablename, 数据表的名称。这个名字可以是任何你想要的名字,只要它是一个合法的表名并且在你的数据库中没有被使用过。

在这个示例中,缓存表的名字是 my_cache_table:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}
"""

2、文件系统缓存

基于文件的缓存后端序列化和存储每个缓存值作为一个单独的文件。其实我不认为我会用到这种方法。

3、内存缓存

内存缓存是最常用的,它也分为本地内存和Memcached以及redis等等。
本地内存:如果在settings中不设置缓存,系统会默认采用这种方式。 最好的方式就是使用Memcached.如何设置在上面也已经说了。

上面说了缓存的存储种类,下面说说缓存的使用场景。

4)场景

(1)站点及缓存

MIDDLEWARE_CLASSES 设置中添加 django.middleware.cache.CacheMiddleware , 就象下面的例子一样:

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',          # 注意位置,在前
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',       # 注意位置,在后
)

####(2)单个View缓存

1.视图缓存

from django.views.decorators.cache 
import cache_page
@cache_page(60 * 15)
def my_view(request):...

cache_page接受一个参数:timeout,秒为单位。在前例中,“my_view()”视图的结果将被缓存 15 分钟 (注意为了提高可读性我们写了 60 * 15 . 60 * 15 等于 900 –也就是说15分钟等于60秒乘15.)

和站点缓存一样,视图缓存与 URL 无关。如果多个 URL 指向同一视图,每个URL将会分别缓存。 继续 my_view范例,如果 URLconf 如下所示:

urlpatterns = [url(r'^foo/([0-9]{1,2})/$', my_view),] 那么正如你所期待的那样,发送到 /foo/1/ and /foo/23/ 会被分别缓存。但是一旦一个明确的 URL (e.g., /foo/23/) 已经被请求过了, 之后再度发出的指向该 URL 的请求将使用缓存。

2.URLConf缓存
urlpatterns = ('',(r'^foo/(\d{1,2})/$', cache_page(60 * 15)(my_view)),)

3.模板碎片缓存

{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}

    #-*-coding:utf-8-*-

"""
    cache.py
    the cache is used to cache some view functions inorder to avoid wasting resources when requesting the same one
    in short serid time.


"""

4、数据库查询缓存

    import functools
    import hashlib
    import logging


    logger = logging.getLogger(__name__)

    class Cache(object):

        def __init__(self):
            self._set_cache()

        def _set_cache(self):
            try:
                self.cache = caches['memcache']
            except:
                self.cache = caches['default']

        def get(self,*args,**kwargs):
            #Proxy function for some cache
            self.cache.get(*args,**kwargs)

        def set(self,*args,**kwargs):
            #Proxy function for some cache
            self.cache.set(*args, **kwargs)

        def delete(self,key):
            #Proxy function for some cache
            pass

        def clear(self):
            #Proxy function for some cache
            pass

        def create_cache_key(self,key):
            return hashlib.md5(key).hexdigest()

        def cached(self,timeout=None):
            #decorator for view functions
            def decorator(func):
                @functools.wraps(func)
                def wrapper(*args,**kwargs):
                    try:
                        cache_key = self.create_cache_key(repr((func,args,kwargs))
                        rv = self.cache.get(cache_key)
                    except Exception:
                        logger.exception('Exception possibly due to cache backend.')
                        return func(*args,**kwargs)
                    if rv is None:
                        rv = func(*args,**kwargs)
                        try:
                            self.cache.set(cache_key,rv,timeout)
                        except Exception:
                            logger.exception('Exception possibly due to cache backend.')
                            return rv
                    return rv

                return wrapper
            return decorator
    
<pre><code>
mycache = Cache()


@classmethod
@mycache.cached(300)
def get_hottest_articles(cls,number=5):
    return cls.objects.filter(status=0).values('title','alias','read_times').order_by('-read_times')[:number]

@classmethod
def get_latest_articles(cls,number=5):
    return cls.objects.filter(status=0).values('title','alias','pub_time').order_by('-pub_time')[:number]

@property
def pre_article(self):
    return Article.objects.filter(id__lt=self.id,status=0).order_by('id').first()
</code></pre>

上述代码对最热文章和最新文章进行了缓存。、

5、安全性

缓存一定要考虑安全性,因为有些数据是不应该存储在缓存中,或者至少不能存储在公共缓存中。

因此Web应用程序需要一种方法来告诉缓存哪些数据是私有的,哪些是公共的。

Django可以指示页面的缓存应为“私有”。要在Django中执行此操作,请使用cache_control视图装饰器。例:

from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    # ...

6、缓存中的Vary头部

正常情况下,对于同一请求,我们认为缓存就是一致的,Django的缓存系统使用请求的url创建其缓存密钥。这意味着对该网址的每个请求都将使用相同的缓存版本,而不考虑用户代理的差异(例如Cookie或语言首选项)。但有时候我们需要根据用户喜好缓存不同内容。比如不同的浏览器,或者不同的Cookie。用法:

@vary_on_cookie
def my_view(request):
    # ...

@vary_on_headers('Cookie')
def my_view(request):
    # ...
--------EOF---------
微信分享/微信扫码阅读