Django 2.0 學習(18):Django 緩存、信號和extra

Django 緩存、信號和extra

Django 緩存

因爲Django是動態網站,因此每次請求均會去數據庫進行相應的操做,當程序訪問量大時,耗時必然會顯著增長。最簡單的解決方法是:使用緩存,緩存將某個views的返回值保存在內存或者redis/memcache中,短期內再次訪問該網頁時,再也不去執行viwes中的操做,而是直接從內存或緩存數據庫中獲取內容,並返回。
Django中提供了6種緩存方式:html

  • 開發調試
  • 內存
  • 文件
  • 數據庫
  • Memcache緩存(python-memcached模塊)
  • Memcache緩存(pylibmc模塊)

1.開發調試python

# 此爲開始調試用,內部不作任何操做
# 配置
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.dummy.DummyCache",   # 引擎
        "TIMEOUT": 300, # 緩存超時時間(默認300,None表示永不過時,0表示當即過時)
        "OPTIONS": {
            "MAX_ENTRIES": 300, # 最大緩存個數(默認300)
            "CULL_FREQUENCY": 3 # 緩存達到最大個數後,剔除緩存個數的比例,即1/CULL_FREQUENCY(默認3)
        },
        "KEY_PREFIX": "",   # 緩存key的前綴(默認空)
        "VERSION": 1,   # 緩存key的版本(默認1)
        "KEY_FUNCTION" 函數名  # 生成key的函數(默認函數會生成爲:【前綴:版本:key】)
    }
}

    # 自定義key
def default_key_func(key, key_prefix, version):
        """
        Default function to generate keys.
 
        Constructs the key used by all other methods. By default it prepends
        the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
        function with custom key making behavior.
        """
        return '%s:%s:%s' % (key_prefix, version, key)
 
def get_key_func(key_func):
        """
        Function to decide which key function to use.
 
        Defaults to ``default_key_func``.
        """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
        return default_key_func

2.內存redis

# 此緩存將內容保存在內存的變量中
# 配置
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake", # 給緩存放置的內存區設置名稱
    }
}
# 注:其餘配置同開發調試版本

3.文件sql

# 此緩存將內容保存在文件中
# 配置
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",    # 配置緩存存放的目錄
    }
}
# 注:其餘配置同開發調試版本

4.數據庫數據庫

# 此緩存將內容保存在數據庫中
# 配置
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "my_cache_table", # 數據庫表
    }
}
# 注:須要執行建立表命令python manage.py createcachetable,這樣會額外建立一張表來存放緩存數據

5.Memcache緩存(python-memcached模塊)django

# 此緩存使用python-memcached模塊鏈接memcache
CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
 
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }  
 
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

6.Memcache緩存(pylibmc模塊)瀏覽器

# 此緩存使用pylibmc模塊鏈接memcache
 CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
 
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '/tmp/memcached.sock',
        }
    }  
 
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

上面都是一些基本的配置,更重要的是配置以後去應用。緩存

應用
1.全站使用緩存app

# 使用中間件,通過一系列的認證等操做,若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶;當返回給用戶以前,判斷緩存中是否已經存在,若是不存在則UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存

MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",    # 放到第一個中間件位置
    # 其餘中間件 ...
    "django.middleware.cache.FetchFromCacheMiddleware",    # 放到最後一個
]

CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""

2.單獨視圖緩存框架

# 方式一:裝飾器
from django.views.decorators.cache import cache_page

@cache_page(60*15)
def my_view(request):
    ...

# 方式二:裝飾器的另外一種寫法
from django.views.decorators.cache import cache_page

urlpatterns = [
    url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]

3.局部視圖緩存

# 1.引入TemplateTag
{% load cache %}

# 2.使用緩存
{% cache 5000 緩存key %}    # 緩存5秒
    緩存內容
{% endcache %}

Django 信號

Django中提供了"信號調度",用於在框架執行操做時解耦,某些動做發生的時候,系統會根據信號定義的函數執行相應的操做。
Django中內置的信號

Model_signals
pre_init        # Django中的model對象執行其構造方法前,自動觸發
psot_init       # Django中的model對象執行其構造方法後,自動觸發  
pre_save        # Django中的model對象保存前,自動觸發
post_save       # Django中的model對象保存後,自動觸發
pre_delete      # Django中的model對象刪除前,自動觸發
post_delete     # Django中的model對象刪除後,自動觸發
m2m_changed     # Django中的model對象使用m2m字段操做數據庫得第三張表(add,remove,clear,update),自動觸發
class_prepared  # 程序啓動時,檢測到已註冊得model類,對於每一個類,自動觸發

Management_signals
pre_migrate     # 執行migrate命令前,自動觸發
post_migrate    # 執行migrate命令後,自動觸發

Request/Response_signals
request_started # 請求到來前,自動觸發
request_finished    # 請求結束後,自動觸發
got_request_exception   # 請求異常時,自動觸發

Test_signals
setting_changed     # 配置文件改變時,自動觸發
template_rendered   # 模板執行渲染操做時,自動觸發

Database_Wrapped
connection_created  # 建立數據庫鏈接時,自動觸發

對於Django內置的信號,僅需註冊指定信號,當程序執行相應操做時,系統會自動觸發註冊函數。例如:建立數據庫記錄,觸發pre_save和post_save信號
建立一個Django項目,配置好路由映射,models.py中的代碼:

from django.db import models

class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length64)

views.py中的代碼:

from django.shortcuts import render,HttpResponse
from app_name import models

def index(request):
    models.UserInfo.objects.create(name="abc", password="abc123")
    return HttpResponse("ok")

項目的__init__.py文件中代碼:

from django.db.models.signals import pre_save, post_save

def pre_save_func(sender, **kwargs):
    print("pre_save_func")
    print("pre_save_msg:", sender, kwargs)

def post_save_func(sender, **kwargs):
    print("post_save_func")
    print("post_save_msg:", sender, kwargs)

pre_save.connect(pre_save_func)     # models對象保存前觸發callback函數
post_save.connect(post_save_func)       # models對象保存後觸發函數

建立一個index.html頁面,用瀏覽器打開這個項目,在服務端後臺打印信息以下:

pre_save_func
pre_save_msg: <class 'app01.models.UserInfo'> {'signal': <django.db.models.signals.ModelSignal object at 0x0000000002E62588>, 
'instance': <UserInfo: UserInfo object>, 'raw': False, 'using': 'default', 'update_fields': None}

post_save_func
post_save_msg: <class 'app01.models.UserInfo'> {'signal': <django.db.models.signals.ModelSignal object at 0x0000000002E62630>, 
'instance': <UserInfo: UserInfo object>, 'created': True, 'update_fields': None, 'raw': False, 'using': 'default'}

比較打印的結果,能夠看到models對象保存後,在打印信息裏包含一個"create=True"的鍵值對:

也可使用裝飾器來觸發信號,把上面__init__.py中的代碼修改:

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def callback(sender, **kwargs):
    print("Request finished!")

則在本次請求結束後會自動觸發callback函數,在後臺打印"request finished"這句話。

自定義信號
1.定義信號:新建一個項目,配置好路由,在項目根目錄下建立一個signals_test.py文件,內容爲

import django.dispatch

action = django.dispatch.Signal(providing_arg=["hello", "world"])

2.註冊信號:項目應用下的__init__.py文件內容

from signal_test import action

def pre_save_func(sender, **kwargs):
    print("pre_save_func")
    print("pre_save_msg:", sender, kwargs)

action.connect(pre_save_func)

3.觸發信號:views視圖函數內容

from signal_test import action

action.send(sender="python", helle="hello", world="world")

用瀏覽器打開index.html頁面,後臺打印信息以下:

pre_save_func 
pre_save_msg: python {'signal': <django.dispatch.dispatcher.Signal object at 0x000000000391D710>, 'hello': 'hello', 'world': 'world'}

因爲內置信號的觸發已經集成到Django中,因此會自動調用。而對於自定義信號須要在任意位置觸發。

Django extra

extra

extra(select=None, where=Noe, params=None, tables=None, order_by=None, select_params=None)

有些狀況下,Django的查詢語法難以表達複雜的WHERE子句,對於這種狀況,Django提供了extra() QuerySet修改機制,它能在QuerySet生成的SQL從句中注入新子句。extra能夠指定一個或多個參數,例如:select,where or tables。這些參數都不是必須的,可是至少要使用一個。注意:這些額外的方式對不一樣的數據庫引擎可能存在移植性問題(由於咱們在顯示的書寫SQL語句),除非萬不得已,儘可能避免這樣作

參數select:select參數能夠在select從句中添加其餘字段信息,他應該是一個字典,存放着屬性名到SQL從句的映射:

queryRes = models.Article.objects.extra(select={"is_recent": "create_time > '2017-09-15'"})

結果集中每一個entry對象都有一個額外的屬性is_recent,它是一個布爾值,表示Article對象的建立時間是否晚於2017-09-05。

practice

# in sqlite
article_obj = models.Article.objects.filter(nid=1).extra(select={"standard_time": "strftime('%%Y-%%m-%%d', create_time)"}).values("standard_time", "nid", "title")

# <QuerySet [{"title": "python 入門教程", "standard_time": "2017-05-12", "nid": 1}]>

參數where/tables:可使用where定義顯示SQL where子句 - 也許執行非顯示鏈接。可使用tables手動將表添加到SQL FROM子句。where和tables都接受字符串列表,全部where參數均爲"與"任何其餘搜索條件,例如:

query_res = models.Article.objects.extra(where=["nid in (1, 5) OR title like "py%", "nid>2"])

綜合例子

current_user = models.UserInfo.objects.filter(username=username).first()        # 當前用戶

models.Article.objects.all()        # 查出每一篇文章
models.Article.objects.all().filter(username=current_user)        # 查出當前用戶的全部文章
models.Article.objects.all().filter(username=current_user).extra(select={"filter_create_date": "strftime('%%Y/%%m', create_time)"}).values_list("filter_create_date")        # 查出當前用戶全部文章的create_time,而且只取出年份和月份

# extra是用來進行過濾的,參數select必須等於一個字典(轉成sql的where語句去執行,查出create_time,而後轉成本身格式化的時間)
models.Article.objects.all().filter(user=current_user).extra(select={"filter_create_date": "strftime('%%Y%%m', create_time)"}).values_list("filter_create_date").annotate(Count("title"))        # 按照查詢出來的年份和月份進行分組,而且顯示文章個數
相關文章
相關標籤/搜索