1. 程式人生 > >Python自動化開發學習22-Django上

Python自動化開發學習22-Django上

python django

session

上節已經講了使用Cookie來做用戶認證,但是
Cookie的問題
缺點:敏感信息不適合放在cookie裏,敏感信息只能放在服務器端
優勢:把部分用戶數據分散的存放在每個客戶端,減輕服務端的壓力
Cookie是保存在用戶瀏覽器端的鍵值對,Session是保存在服務器端的鍵值對。
Session依賴Cookie,Cookie保存隨機字符串,憑借這個隨機字符串獲取到服務器端Session裏的內容。
用Session來優化用戶登錄:用戶登錄後,生成一個隨機字符串,通過Cookie發送給客戶端保存。服務器端維護一個字典,字典的key就是這個隨機生成的字符串,字典的value是另外一個字典,在服務器端保存各種用戶的敏感信息。

Django的sessiong默認是保存在數據庫中的,所以要使用session需要先執行生成數據庫的2條命令:

python manage.py makemigrations
python manage.py migrate

即使你沒有寫任何一張表,也會默認生成一些表,其中 django_session 表中就是存放session信息的。

操作

session裏存放的就是鍵值對,所以操作起來也跟字典一樣。隨便寫一個登錄的頁面,用下面的處理函數進行操作:

USER_INFO = {
    ‘test‘: {‘pass‘: "test123"},
    ‘user‘: {‘pass‘: "user123"},
}

def login(request):
    if request.method == "GET":
        return render(request, ‘login.html‘)
    elif request.method == ‘POST‘:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        user_obj = USER_INFO.get(user)
        if user_obj and user_obj.get(‘pass‘) == pwd:
            # 1.生成隨機字符串
            # 2.寫到用戶瀏覽器Cookie
            # 3.把隨機字符串作為key保存到session中
            # 4.設置對應的value
            # 理論上是要完成上面4個步驟
            # 但是都封裝好了,用的時候只需要1步,就能完成上面的操作,下面是設置session的值
            request.session[‘username‘] = user
            request.session[‘is_login‘] = True
            return redirect(‘/welcome/‘)
        else:
            return render(request, ‘login.html‘)

def welcome(request):
    # 獲取session的值
    if request.session.get(‘is_login‘):
        return HttpResponse(‘Welcome %s‘ % request.session[‘username‘])
    else:
        return HttpResponse(‘登錄失敗‘)

登錄頁面:

<form action="/login/" method="POST">
    <p><input type="text" name="user" placeholder="用戶名"></p>
    <p><input type="password" name="pwd" placeholder="密碼"></p>
    <p><input type="submit" value="登錄"></p>
</form>

上面用到了設置數據的操作和獲取數據的操作。其他的操作方法見下面整理的內容。

獲取、設置、刪除Session中數據:

  • request.session[‘k1‘] :獲取值,如果key不存在會報錯
  • request.session.get(‘k1‘,None) :獲取值,如果key不存在,在獲取到默認值。默認值默認是None
  • request.session[‘k1‘] = 123 :設置值
  • request.session.setdefault(‘k1‘,123) :設置默認值,就是如果這個key有值就不改變,沒有值就設置為第二個參數的值
  • del request.session[‘k1‘] :刪除值

對所有鍵、值、鍵值對的操作:

  1. request.session.keys()
  2. request.session.values()
  3. request.session.items()
  4. request.session.iterkeys()
  5. request.session.itervalues()
  6. request.session.iteritems()

iterkeys 和 keys 的區別,iterkeys 返回一個叠代器,而 keys 返回一個列表。
其他:

  • request.session.session_key :獲取當前用戶的隨機字符串。但是一般這個用不著,不過是可以獲取到的
  • request.session.clear_expired() :將所有Session失效日期小於當前日期的數據刪除。過期的session,數據庫是沒有自動清除的機制的。不過緩存系統是有的
  • request.session.exists("session_key") :檢查作為參數的隨機字符串在數據庫中是否存在。一般也用不著,因為獲取值的時候已經包含這個判斷了,沒有會返回None
  • request.session.delete("session_key") :刪除參數的Session的所有數據
  • request.session.clear() :刪除當前用戶的所有Session數據。和delete方法比較,delete需要提供session_key。而clear方法會先去獲取到當前用戶的key,然後delete

request.session.set_expiry(value) :設置超時時間

  • 如果value是個整數,session會在些秒數後失效。
  • 如果value是個datatime或timedelta,session就會在這個時間後失效。
  • 如果value是0,用戶關閉瀏覽器session就會失效。
  • 如果value是None,session會依賴全局session失效策略。

下面的內容在 Lib/site-packages/django/conf/global_settings.py 這個文件裏,是默認配置。要修改的話,就在我們的 settings.py 裏設置一個值:

############
# SESSIONS #
############

# Cache to store session data if using the cache session backend.
SESSION_CACHE_ALIAS = ‘default‘
# Cookie name. This can be whatever you want.
SESSION_COOKIE_NAME = ‘sessionid‘  # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串
# Age of cookie, in seconds (default: 2 weeks).
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2  # Session的cookie失效日期,這裏默認的是2周
# A string like "example.com", or None for standard domain cookie.
SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名
# Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_SECURE = False  # 是否Session的cookie只支持http傳輸
# The path of the session cookie.
SESSION_COOKIE_PATH = ‘/‘  # Session的cookie保存的路徑
# Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http傳輸
# Whether to save the session data on every request.
SESSION_SAVE_EVERY_REQUEST = False  # 是否每次請求都保存Session,默認修改之後才保存
# Whether a user‘s session cookie expires when the Web browser is closed.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否關閉瀏覽器使得Session過期
# The module to store session data
SESSION_ENGINE = ‘django.contrib.sessions.backends.db‘  # 默認引擎
# Directory to store session files if using the file session module. If None,
# the backend will use a sensible default.
SESSION_FILE_PATH = None  # 緩存文件路徑,如果為None,則使用tempfile模塊獲取一個臨時地址
# class to serialize session data
SESSION_SERIALIZER = ‘django.contrib.sessions.serializers.JSONSerializer‘

CSRF(跨站請求偽造)

客戶端第一次發起GET請求後,不僅返回了頁面,同時還會返回一串加密字符串。之後客戶端再提交數據的時候,需要把之前收到的字符串一並提交,這樣服務器端的CSRF就會對字符串進行驗證,確認收到的數據是之前發起請求的客戶端提交上來的。
無CSRF的隱患,如果沒有CSRF,正常客戶端提交數據是提交給當前訪問的網站。但是也是可以提交到別的網站去的(你可以提交到任何地方)。雖然你可以提交過去,但是這種方式你沒有目標網站給你的加密字符串的,所以開啟CSRF之後,直接就把這部分請求攔截了。

開啟CSRF

django為用戶實現防止跨站請求偽造的功能,通過中間件 django.middleware.csrf.CsrfViewMiddleware 來完成。而對於django中設置防跨站請求偽造功能有分為全局和局部。
全局,settings.py 文件中的MIDDLEWARE(中間件)裏,是一個列表,之前都是先把下面的這行註釋掉的:

‘django.middleware.csrf.CsrfViewMiddleware‘,

局部
導入模塊:from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt :為當前函數強制設置防跨站請求偽造功能,即便settings中沒有設置全局中間件。
@csrf_protect :取消當前函數防跨站請求偽造功能,即便settings中設置了全局中間件。

應用-Form方式提交

第一次請求獲取到的字符串在哪裏,如何在提交form表單的時候加上獲取的字符串,從而通過CSRF的驗證。做一個簡單的登錄頁面,上節有,然後去 settings.py 文件裏開啟之前一直註釋掉的CSRF,按下面的方式提交:

<form action="/login/" method="POST">
    {# 加上下面的標簽就可以了 #}
    {% csrf_token %}
    <p><input type="text" name="username" placeholder="用戶名"></p>
    <p><input type="password" name="password" placeholder="密碼"></p>
    <p><input type="submit" value="登錄"></p>
</form>

如果form不帶 csrf_token ,那麽會返回403錯誤。這裏查看客戶端form標簽內的源碼可以發現,系統幫我們生成了一個隱藏的input標簽

<input name="csrfmiddlewaretoken" type="hidden" value="I9pZVK6UYWgHHPfUQxju79pWu65xOrb793mpWXON1n4HgeTGzheJ78HHQBgu6cB8">

應用-Ajax方式提交

使用Ajax方式提交,需要先獲取到csrf的字符串,客戶端收到服務器端的字符串後,是保存在cookie裏的,所以可以到cookie裏去獲取到。提交的時候也要帶上這個csrf字符串,加一條請求頭: headers: {‘X-CSRFtoken‘: csrftoken},
在原來的頁面裏,添加Ajax請求的按鈕,並且綁定事件。驗證不通過重新加載頁面,驗證通過則用 location.href 頁面跳轉:

<body>
<form action="/login/" method="POST">
    {% csrf_token %}
    <p><input type="text" name="user" placeholder="用戶名"></p>
    <p><input type="password" name="pwd" placeholder="密碼"></p>
    <p>
        <input type="submit" value="登錄">
        <input id="btn" type="button" value="Ajax提交">
    </p>
</form>
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
    $(function () {
        $(‘#btn‘).click(function () {
            var csrftoken = $.cookie(‘csrftoken‘);
            var user = $("input[name=‘user‘]").val();
            var pwd = $("input[name=‘pwd‘]").val();
            $.ajax({
                url: ‘/login/‘,
                type: ‘POST‘,
                data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
                headers: {‘X-CSRFtoken‘: csrftoken},
                success: function (arg) {
                    if(arg === ‘OK‘){
                        location.href = ‘/welcome/‘
                    }else{
                        location.reload()
                    }
                }
            })
        })
    })
</script>
</body>

處理函數也在原來的基礎上添加。通過 is_ajax = request.POST.get(‘ajax‘) 判斷是否是ajax提交的請求。最後在return的時候返回不同的結果,其他都不變:

def login(request):
    if request.method == "GET":
        return render(request, ‘login.html‘)
    elif request.method == ‘POST‘:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        is_ajax = request.POST.get(‘ajax‘)
        user_obj = USER_INFO.get(user)
        if user_obj and user_obj.get(‘pass‘) == pwd:
            # 1.生成隨機字符串
            # 2.寫到用戶瀏覽器Cookie
            # 3.把隨機字符串作為key保存到session中
            # 4.設置對應的value
            # 但是只需要一步,就能完成上面的操作,下面是設置session的值
            request.session[‘username‘] = user
            request.session[‘is_login‘] = True
            print(request.session)
            return HttpResponse(‘OK‘) if is_ajax else redirect(‘/welcome/‘)
        else:
            print(‘error‘)
            return HttpResponse(‘error‘) if is_ajax else render(request, ‘login.html‘)

統一設置ajax的csrf,如果頁面裏有多個ajax請求,可以統一進行設置。再或者比如之前的練習,已經寫好了不帶csrf的ajax請求,現在也不要去裏面改了,用下面的方法統一加上。就是在ajax發送前,設置請求頭加上X-CSRFtoken這個key並且設置值:

<script>
    $(function () {
        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                // xhr 就是 XHLHttpRequest 是一個對象
                xhr.setRequestHeader(‘X-CSRFtoken‘, $.cookie(‘csrftoken‘))  // 設置請求頭
            }
        });
        $(‘#btn‘).click(function () {
            // var csrftoken = $.cookie(‘csrftoken‘);
            var user = $("input[name=‘user‘]").val();
            var pwd = $("input[name=‘pwd‘]").val();
            $.ajax({
                url: ‘/login/‘,
                type: ‘POST‘,
                data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
                // headers: {‘X-CSRFtoken‘: csrftoken},
                success: function (arg) {
                    if(arg === ‘OK‘){
                        location.href = ‘/welcome/‘
                    }else{
                        location.reload()
                    }
                }
            })
        })
    })
</script>

上面的代碼還要再優化一個,只有POST請求需要加csrf,GET請求是不需要加的。但是在上面的代碼裏所有的ajax請求的前面都會在請求頭加上csrf,這裏要再做個判斷。除了POST和GET還有其他好幾種請求,一起做判斷:

<script>
    function csrfSafeMethod(method) {
        // 下面的這幾類HTTP請求是不需要加CSRF驗證的,這個是官網的一個例子的用法
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $(function () {
        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                // xhr 就是 XHLHttpRequest 是一個對象
                // settings 裏就是下面的$.ajax({})大括號裏的內容,這裏要獲取type檢查是否需要加csrf
                if (!csrfSafeMethod(settings.type) && !this.crossDomain){
                    // 滿足上面的條件才需要設置請求頭
                    xhr.setRequestHeader(‘X-CSRFtoken‘, $.cookie(‘csrftoken‘))  // 設置請求頭
                }

            }
        });
        $(‘#btn‘).click(function () {
            // var csrftoken = $.cookie(‘csrftoken‘);
            var user = $("input[name=‘user‘]").val();
            var pwd = $("input[name=‘pwd‘]").val();
            $.ajax({
                url: ‘/login/‘,
                type: ‘POST‘,
                data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
                // headers: {‘X-CSRFtoken‘: csrftoken},
                success: function (arg) {
                    if(arg === ‘OK‘){
                        location.href = ‘/welcome/‘
                    }else{
                        location.reload()
                    }
                }
            })
        })
    })
</script>

中間件

在settings.py文件裏有一個列表,裏面列個各種中間件,包括上面將過的csrf。下面是原始的settings.py文件裏的中間件的內容:

MIDDLEWARE = [
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
]

用戶發起請求,到請求到達Views之前,首先要通過這寫中間件。這些中間件就是一個類,經過就是要調用這些中間件類裏的方法,process_request方法。請求的返回也要先通過這些中間件,調用process_response方法,然後再發送給用戶。
其他名稱在其他的Web框架裏,也有中間件,但是名字可能叫做:管道(Pipeline),HttpHandler。

自定義中間件

先以csrf的中間件舉例,代碼中引用的字符串是:‘django.middleware.csrf.CsrfViewMiddleware‘ 。這個就是路徑和類名。
可以到python的模塊中找到這個文件和類。在 \Lib\site-packages\django\middleware\csrf.py 這個文件裏有一個類 class CsrfViewMiddleware(MiddlewareMixin): ,這個類裏就有process_request方法和process_response方法。並且可以看到這個類是繼承了MiddlewareMixin這個類的,查看這個父類,可以看到這個類裏就是調用上面的2個方法。
要寫自己的中間件,就是要定義一個類,繼承MiddlewareMixin。並且在類裏寫上process_request方法和process_response方法。
在項目下創建Middle文件夾來存放自定義的中間件,創建m1.py文件:

from django.utils.deprecation import MiddlewareMixin

class Row1(MiddlewareMixin):
    def process_request(self, request):
        print(‘ROW1_reuquest‘)

    def process_response(self, request, response):
        print(‘ROW1_response‘)
        return response

class Row2(MiddlewareMixin):
    def process_request(self, request):
        print(‘ROW2_reuquest‘)

    def process_response(self, request, response):
        print(‘ROW2_response‘)
        return response

上面寫了2個類,就是2個中間件了。然後去setting.py裏註冊一下你的中間件:

MIDDLEWARE = [
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
    ‘Middle.m1.Row1‘,
    ‘Middle.m1.Row2‘,
]

現在再訪問你之前的頁面,就能看到中間件print的信息了,並且可以看到觸發的順序:

ROW1_reuquest
ROW2_reuquest
[11/Apr/2018 11:24:04] "GET /login/ HTTP/1.1" 200 492
ROW2_response
ROW1_response

先觸發request方法,並且是寫在前面的中間件先觸發。然後再是收到GET請求。最後觸發response方法。
中間件的應用場景:通過中間件可以對所有的請求做一個統一的操作,比如黑名單過濾。
中間件中可以定義以下方法:

  • process_request(self, request) :處理請求前最先處理
  • process_view(self, request, callback, callback_args, callback_kwargs) :在process_request全部執行之後執行,還是從上到下
  • process_template_response(self, request, response) :如果views返回對象中有render方法,則會執行
  • process_exception(self, request, exception) :當views裏出現異常時觸發。exception就是異常的信息
  • process_response(self, request, response) :返回結果後到客戶端收到響應前處理

常用的就2個,其他了解一下

緩存

由於Django是動態網站,所以每次請求均會去數據進行相應的操作,當程序訪問量大時,耗時必然會更加明顯,最簡單解決方式是使用緩存。緩存是將某個views的返回值保存至內存或者memcache中,5分鐘內(默認設置)再有人來訪問時,則不再去執行views中的函數,而是直接從內存或者Redis中之前緩存的內容拿到,並返回。
Django中提供了6種緩存方式:

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

配置

開發調試以及默認配置
實際內部不做任何操作,就是調試用的。只有第一個引擎是必須要設置的,其他都有默認配置(上面也是照著默認設的),不寫就是使用默認設置,或者寫上你要修改的設置。其它緩存方式的配置也是一樣的,就是把引擎換一下,再加一個 ‘LOCATION‘ 就是緩存存放的位置:

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

默認的 ‘KEY_FUNCTION‘ 是在 Lib\site-packages\django\core\cache\backends\base.py 文件裏的default_key_func函數,就是生成一個包含 key_prefix, version, key 這3個變量的字符串返回。這裏可以用我們自己的寫的函數生成自己想要的樣式:

def default_key_func(key, key_prefix, version):
    """
    Default function to generate keys.

    Construct the key used by all other methods. By default, prepend
    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)

內存

# 此緩存將內容保存至內存的變量中
# 配置:
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.locmem.LocMemCache‘,
        ‘LOCATION‘: ‘unique-snowflake‘,  # 這個必須是全局唯一的變量名,實際就是在內存中維護一個字典,這個是變量名
    }
}

文件

# 此緩存將內容保存至文件
# 配置:
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘,
        ‘LOCATION‘: ‘/var/tmp/django_cache‘,  # 緩存文件的路徑
    }
}

數據庫

# 此緩存將內容保存至數據庫
# 配置:
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.db.DatabaseCache‘,
        ‘LOCATION‘: ‘my_cache_table‘,  # 數據庫表,這裏是表名,使用前先要創建表,命令在下面
    }
}
# 註意:使用前先執行創建表命令 python manage.py createcachetable

Memcache緩存(python-memcached模塊)

# 此緩存使用python-memcached模塊連接memcache
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
        ‘LOCATION‘: ‘127.0.0.1:11211‘,  # ip地址和端口
    }
}
# 或者是連接本地文件
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
        ‘LOCATION‘: ‘unix:/tmp/memcached.sock‘,
    }
}
# 或者是使用分布式的memcache
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
        ‘LOCATION‘: [
            ‘172.19.26.240:11211‘,
            ‘172.19.26.242:11211‘,
        ]
    }
}
# 或者是使用分布式的memcache,還可以帶權重(權重是memcache做的)
CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
        ‘LOCATION‘: [
            (‘172.19.26.240:11211‘, 2),
            (‘172.19.26.242:11211‘, 3),
        ]
    }
}

Memcache緩存(pylibmc模塊)
和上面差不多,只是用的是不同的python模塊,所以引擎要變一下。

# 此緩存使用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‘,
        ]
    }
}
# 權重也是有的

應用

準備測試環境
使用文件的形式來測試,在項目下創建一個cache文件夾來存放緩存文件,配置如下:

CACHES = {
    ‘default‘: {
        ‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘,
        ‘LOCATION‘: os.path.join(BASE_DIR, ‘cache‘),  # 緩存文件的路徑
    }
}

然後寫一個顯示當前時間戳的處理函數:

def cache(request):
    import time
    ctime = time.time()
    return render(request, ‘cache.html‘, {‘ctime‘: ctime})

頁面只要把時間戳顯示出來:

<body>
<h1>{{ ctime }}</h1>
<h1>{{ ctime }}</h1>
</body>

現在訪問頁面會看到一個時間戳,並且刷新頁面時間戳也會刷新,說明沒有走緩存。因為上面我們只是把緩存配置上了,還是還沒應用上。

全站應用緩存
這個太粗糙了,下面有更加精細的設置。
要給所有的請求都應用上緩存,這個功能適合放在中間件裏。中間件裏需要寫2個中間件
查緩存,收到用戶請求先查找緩存,如果命中則直接返回緩存的內容。這個中間件要放在後面的位置,只有通過了一系列驗證的中間件後才能給用戶返回數據。
寫緩存,返回給用戶之前沒有緩存的內容的同時,需要把這個內容寫到緩存裏去,那麽下次就有緩存了。這個中間件要放在靠前的位置,通過了一系列其他中間件加工之後,把最終返回的內容緩存起來。

MIDDLEWARE = [
    ‘django.middleware.cache.UpdateCacheMiddleware‘,
    # 其他中間件...
    ‘django.middleware.cache.FetchFromCacheMiddleware‘,
]

視圖應用緩存1:處理函數前加裝飾器

from django.views.decorators.cache import cache_page
@cache_page(10)  # timeout必填,單位秒
def func(request):
    pass

視圖應用緩存2:修改urls.py的

from django.views.decorators.cache import cache_page
urlpatterns = [
    path(‘admin/‘, admin.site.urls),
    path(‘cache/‘, cache_page(5)(views.cache)),
]

局部視圖緩存:
就是在模板語言裏聲明,頁面裏的哪些內容是要應用緩存的

<body>
{#要應用緩存,先load一下cache#}
{% load cache %}
<h1>{{ ctime }}</h1>
{#然後在需要應用緩存的內容的前後加上標簽#}
{#下面的c1是自定義的key,這裏緩存的值的key貌似無法根據配置自動生成#}
{% cache 5 c1 %}
<h1>{{ ctime }}</h1>
{% endcache %}
</body>

首先要load緩存,然後在要應用緩存的內容前後用標簽包起來。這裏需要定義timeout時間和key。
這個局部的應用場景還是很多的,只緩存頁面中不會頻繁發生變化的內容,而經常會變的內容則不緩存

信號

Django中提供了“信號調度”,用於在框架執行操作時解耦。通俗來講,就是一些動作發生的時候,信號允許特定的發送者去提醒一些接受者。

Django內置信號

  • Model signals
    • pre_init : django的modal執行其構造方法前,自動觸發
    • post_init : django的modal執行其構造方法後,自動觸發
    • pre_save : django的modal對象保存前,自動觸發
    • post_save : django的modal對象保存後,自動觸發
    • pre_delete : django的modal對象刪除前,自動觸發
    • post_delete : django的modal對象刪除後,自動觸發
    • m2m_changed : django的modal中使用m2m字段操作第三張表(add,remove,clear)前後,自動觸發
    • class_prepared : 程序啟動時,檢測已註冊的app中modal類,對於每一個類,自動觸發
  • Management signals
    • pre_migrate : 執行migrate命令前,自動觸發
    • post_migrate : 執行migrate命令後,自動觸發
  • Request/response signals
    • request_started : 請求到來前,自動觸發
    • request_finished : 請求結束後,自動觸發
    • got_request_exception : 請求異常後,自動觸發
  • Test signals
    • setting_changed : 使用test測試修改配置文件時,自動觸發
    • template_rendered : 使用test測試渲染模板時,自動觸發
  • Database Wrappers
    • connection_created : 創建數據庫連接時,自動觸發

使用之前先要把對應的模塊導入:

from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import class_prepared

from django.db.models.signals import pre_migrate, post_migrate

from django.core.signals import request_started
from django.core.signals import request_finished
from django.core.signals import got_request_exception

from django.test.signals import setting_changed
from django.test.signals import template_rendered

from django.db.backends.signals import connection_created

註冊函數1
將函數名作為參數完成註冊

# 導入模塊
from django.core.signals import request_started

# 準備好要觸發的函數
def callback(sender, **kwargs):
    print(‘callback‘)
    print(sender, kwargs)

request_started.connect(callback)  # 註冊你的函數
# pre_init.connect(callback2)  # 可以註冊多個,先註冊的先執行

註冊函數2
為函數加上裝飾器也能完成註冊:

# 導入模塊
from django.core.signals import request_started
from django.dispatch import receiver

# 準備好要觸發的函數,並且加上裝飾器
@receiver(request_started)
def callback(sender, **kwargs):
    print(‘callback‘)
    print(sender, kwargs)

自定義信號

要自定義信號需要3步:

  1. 定義信號 :這裏單獨創建一個文件(名字隨意比如sg.py)來存放自定義的信號
  2. 註冊信號 :定義完成好,直接就註冊吧,註冊方法和註冊內置信號一樣
  3. 觸發信號 :在你要觸發的位置,執行一個send()方法。比如下面的例子中是在處理函數中加入了這個信號的觸發

內置信號只需要註冊。而自定義的信號,需要在註冊前先定義好這個信號。之後去你需要的位置加上觸發信號的send()方法:

# sg.py 裏的內容
# 第一步:定義信號
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])  # 這裏要求2個參數

# 第二步:註冊信號
def callback(sender, **kwargs):
    print(‘callback‘)
    print(sender, kwargs)
pizza_done.connect(callback)

# views.py 裏的內容,當然可以是任何地方
# 第三步:觸發信號
# 導入信號
from sg import pizza_done
def my_sig(request):
    # 發送信號,第一個參數是發送者,後面是你自定義的函數要求的參數
    pizza_done.send(sender=‘seven‘, topping=123, size=456)

Python自動化開發學習22-Django上