Django之緩存、信號和圖片驗證碼

1、 緩存

一、 介紹

緩存通俗來講:就是把數據先保存在某個地方,下次再讀取的時候不用再去原位置讀取,讓訪問速度更快。
緩存機制圖解html

 

二、Django中提供了6種緩存方式

  1. 開發調試
  2. 內存
  3. 文件
  4. 數據庫
  5. Memcache緩存(python-memcached模塊)
  6. Memcache緩存(pylibmc模塊)python

 注意:下面對緩存進行配置,其實就是設置了緩存方式,是爲了設置緩存的存放位置,若是沒有設置緩存方式,那麼就默認使用的是本地內存緩存的方式。數據庫

 

三、 配置緩存(在setting中配置)

配置緩存的方式 1. 開發調試 # 此爲開始調試用,實際內部不作任何操做
    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. 內存 # 此緩存將內容保存至內存的變量中
        CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', 'TIMEOUT': 300,  # 緩存超時時間(默認300,None表示永不過時,0表示當即過時)
                'OPTIONS': { 'MAX_ENTRIES': 300,  # 最大緩存個數(默認300)
                    'CULL_FREQUENCY': 3,  # 緩存到達最大個數以後,剔除緩存個數的比例,即:1/CULL_FREQUENCY(默認3)
 }, } } 3. 文件 # 此緩存將內容保存至文件
    # 配置:
 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模塊) # 此緩存使用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', ] } }

 

四、 簡單的應用

配置好了緩存的方式以後,咱們使用緩存機制也是很是簡單的django

1. 給單獨的視圖應用緩存: 粒度適中 方式一:直接給視圖函數添加裝飾器 # views.py
    import datetime from django.views.decorators.cache import cache_page @cache_page(15)  # 緩存15秒後失效
    def test_cache(request): now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') return HttpResponse(now) 方式二:給路由添加 # urls.py
    from myapp.views import test_cache from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^testcache/$', cache_page(15)(test_cache)), ] 方式一和二選擇一種便可 2. 全站應用: 粒度最大(settings.py) 使用中間件,通過一系列的認證等操做,若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶, 當返回給用戶以前,判斷緩存中是否已經存在,若是不存在則UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存(全部視圖都進行緩存) MIDDLEWARE = [ # 站點緩存 , 注意必須在第一個位置
        'django.middleware.cache.UpdateCacheMiddleware', # 其餘中間件...

        # 站點緩存 , 注意必須在最後一個位置
        'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = 300  # 緩存有效時間
    CACHE_MIDDLEWARE_KEY_PREFIX = ""


3. 局部視圖(在HTML頁面設置哪些須要緩存):粒度最細 a. 引入TemplateTag {% load cache %} b. 使用緩存 {% cache 300 '緩存key' %}  # 緩存key的名字能夠是隨意的
 緩存內容 {% endcache %}

 

2、 序列化

一、介紹

關於Django中的序列化主要應用在將數據庫中檢索的數據返回給客戶端用戶,特別的Ajax請求通常返回的爲Json格式。json

 

二、serializers

from django.core import serializers def get_value(request): users = models.User.objects.all() ret = serializers.serialize('json', users) return HttpResponse(ret)

 

三、自定義序列化

因爲json模塊並不能轉換時間類型的數據,所以須要咱們自定義一個類來處理時間類型的數據 import json from datetime import datetime, date data = [  # data數據中有datetime類型的值, json不能直接序列化
    {"pk": 1, "name": "\u83b9\u83b9", "age": 18, 'birth': datetime.now()}, {"pk": 2, "name": "\u5c0f\u5fae", "age": 16, 'birth': datetime.now()}, {"pk": 3, "name": "\u5c0f\u9a6c\u54e5", "age": 8, 'birth': datetime.now()}, {"pk": 4, "name": "qqq", "age": 5, 'birth': datetime.now()}, {"pk": 5, "name": "www", "age": 5, 'birth': datetime.now()} ] # json序列化的時候是調用JSONEncoder這個類的default方法進行序列化的
class JsonCustomEncoder(json.JSONEncoder):  # 自定義一個類,從新json.dumps的default方法

    def default(self, field):  # 循環每一個字段的值
        
        if isinstance(field, datetime):  # 若是這個值是datetime類型,咱們本身把它轉成字符串類型的時間
            return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date):  # 若是這個值是date類型,咱們本身把它轉成字符串類型的時間
            return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field)  # 若是這個值不是時間類型,調用其父類本來的default方法進行序列化

print(json.dumps(data,cls=JsonCustomEncoder))  # cls指定序列化的時候去執行這個類

 

3、 信號

一、介紹

Django中提供了「信號調度」,用於在框架執行操做時解耦。通俗來說,就是一些動做發生的時候,信號容許特定的發送者去提醒一些接受者。
緩存

 

二、內置信號

Model signals pre_init # django的model執行其構造方法前,自動觸發
    post_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)先後,自動觸發
    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 # 建立數據庫鏈接時,自動觸發

 

三、使用

1. 場景:數據庫增長一條數據時,就記錄一條日誌,若不使用信號,則須要在每一個建立語句下面寫記錄日誌的語句。 2. 介紹 對於Django內置的信號,僅需註冊指定信號,當程序執行相應操做時,自動觸發註冊函數 註冊信號,寫入與project同名的文件夾下的_init_.py文件中,也是換數據庫引擎的地方。 3. 註冊信號步驟 1. 導入須要的信號模塊(這裏列出所有模塊,實際開發的時候須要哪一個就導入哪一個) from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared 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 pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created 2. 定義函數來處理信號 # 方法一
from django.db.models.signals import post_save # 函數名可隨意,可是參數(sender, **kwargs)是固定的,就這兩個參數
def callback(sender, **kwargs): print("xxoo_callback") print(sender, kwargs) post_save.connect(callback) # 註冊post_save信號:django的model對象保存後,自動觸發callback函數 # post_save信號中,render就是觸發信號的那一個ORM類(表) # kwargs就是這個類的一些參數:instance是這個類的實例,created:是不是建立操做


# 方法二
from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save) def my_callback(sender, **kwargs): print("xxoo_callback") print(sender, kwargs) # 方法三:指定觸發者
from django.db.models.signals import post_save from django.dispatch import receiver from myapp.models import MyModel # 指定只有MyModel這個類才能觸發這個函數
@receiver(post_save, sender=MyModel) def my_callback(sender, **kwargs): print("xxoo_callback") print(sender, kwargs) # 或者
post_save.connect(callback, sender=MyModel)

 

四、自定義信號

a. 定義信號 在某py文件中定義信號。 import django.dispatch # pizza_done是信號名 # providing_args是傳給信號綁定的函數的kwargs
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) b. 註冊信號 在_init_.py 中註冊信號 from 路徑 import pizza_done def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback) c. 觸發信號 from 路徑 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456) 因爲內置信號的觸發者已經集成到Django中,因此其會自動調用,而對於自定義信號則須要開發者在任意位置觸發。

 

 

4、 ORM性能相關

0、 表結構 class Role(models.Model): name = models.CharField(max_length=32) class User(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() role = models.ForeignKey('Role', null=True, blank=True) 一、 直接查詢--> [ 對象 ] 用的時候注意,只拿本身表中的字段,別跨表,好比all_users有3條數據,user表經過外鍵關聯role表, 若是要跨表拿到role表的name字段: all_users = models.User.objects.all() for user in all_users: print(user.name, user.age, user.role.name) 其實一個進行了四次查詢,第一次查詢出all_users,而後每次的user.role.name都去role表查 二、 要用到跨表字段的時候,使用values或values_list查詢速度更快,只需一次查詢便可--> [{}] all_users = models.User.objects.all().values('name','age','role__name') for user in all_users: print(user['name'], user['age'], user['role__name']) 3、 select_related:連表較少的時候使用,如:外鍵、一對一 查詢的時候把關聯的表也一塊兒查了,也是一次查詢出結果,跟values不一樣的是,能夠直接用點取字段 all_users = models.User.objects.all().select_related('role') for user in all_users: print(user.name, user.age, user.role.name) 4、 prefetch_related:連表較多的時候使用,如:多對多字段和一對多字段 all_users = models.User.objects.all().prefetch_related('role') for user in all_users: print(user.name, user.age, user.role.name) 5、 only:將指定的字段查詢加載出來,後續再訪問指定的字段就不須要再查詢數據庫 all_users = models.User.objects.all().only('name') 用的時候注意,只拿本身指定的字段 6、 defer:將除了指定的字段查詢加載出來,後續再訪問指定的字段就不須要再查詢數據庫(only的反義詞) all_users = models.User.objects.all().defer('name')

 

5、 驗證碼

一、隨機驗證碼python代碼

import random def get_code(): code = ''
    for i in range(6): num = str(random.randint(0, 9))  # 數字
        lower = chr(random.randint(97, 122))  # 小寫字母
        upper = chr(random.randint(65, 90))  # 大寫字母
        c = random.choice([num, lower, upper])  # 隨機選取一個
        code += str(c) return code

 

二、如何生成圖片

1. 驗證碼的形式 回想一下,平時咱們輸入驗證碼的時候,是否是都是看着一張圖片,圖片上顯示驗證碼,咱們看着圖片輸入驗證碼。 固然如今還有滑動的,點擊等等,這裏咱們先學習圖片的形式。 2. 實現步驟 1, 準備一張沒有任何內容的圖片 2, 安裝python專門處理圖片的第三方包 pip install Pillow 3, 包的導入 from PIL import Image, ImageDraw, ImageFont 4, Image:生成一張圖片 ImageDraw:生成一個畫筆,用於在圖片上畫驗證碼 ImageFont:字體的格式和大小 5,示例 from PIL import Image, ImageDraw, ImageFont # 返回隨機的RGB數字
def random_color(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def get_code(): with open('1.png', 'wb') as f: # 第一步:生成一張圖片(畫布)
        # 建立一個隨機顏色的圖片對象
        # 參數:顏色模式,圖片大小,圖片顏色
        img_obj = Image.new('RGB', (250, 35), random_color()) # 第二步:在該圖片對象上生成一個畫筆對象
        draw_obj = ImageDraw.Draw(img_obj) # 使用什麼字體,字體大小,kumo.ttf是我本地下載好的字體文件(sc.chinaz.com)可下載
        font_obj = ImageFont.truetype('static/font/kumo.ttf', 28) # 生成驗證碼
        code = ''
        for i in range(6): num = str(random.randint(0, 9))  # 數字
            lower = chr(random.randint(97, 122))  # 小寫字母
            upper = chr(random.randint(65, 90))  # 大寫字母
            c = random.choice([num, lower, upper])  # 隨機選取一個
            code += str(c) # 用畫筆把驗證碼畫到圖片上
            # 參數:xy:座標,畫在哪一個位置;text:畫的內容;fill:畫什麼顏色;font:字體格式
            draw_obj.text((35 + i*30, 0), c, fill=random_color(), font=font_obj) # 保存圖片
 img_obj.save(f) get_code() 6,缺點 上面的代碼是在你的硬盤上存了一張圖片,若是要在頁面上展現,你還得進行文件的讀, 這樣的話不只浪費硬盤空間,效率還不夠高,所以咱們應該把圖片寫到內存,從內存中取,效率就快不少了, 而後把圖片的驗證碼數據存到session,這樣登陸的時候就能夠校驗了。

 

 

三、在視圖中使用驗證碼

1. urls urlpatterns = [ # 獲取圖片的路由
    url(r'^login/', views.login), url(r'^v_code/', views.v_code), ] 2. 在頁面中點擊驗證碼圖片,刷新驗證碼 <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta http-equiv="content-Type" charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>

<form action='' method='POST'> {% csrf_token %} <input type='text' name='username'>用戶名 <input type='password' name='password'>密碼 <img src="/v_code/" alt="圖片加載失敗" id="v_code">
    <button type="submit">登陸</button>
</form>

<script> img = document.getElementById('v_code'); img.onclick = function () { img.src += '?' } </script>

</body>
</html>


3. 驗證碼視圖函數 from PIL import Image, ImageDraw, ImageFont import random def random_color(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def v_code(request): # 第一步:生成一張圖片(畫布)
    # 建立一個隨機顏色的圖片對象
    # 參數:顏色模式,圖片大小,圖片顏色
    img_obj = Image.new('RGB', (250, 35), random_color()) # 第二步:在該圖片對象上生成一個畫筆對象
    draw_obj = ImageDraw.Draw(img_obj) # 使用什麼字體,字體大小
    font_obj = ImageFont.truetype('static/font/kumo.ttf', 28) # 生成驗證碼
    code = ''
    for i in range(6): num = str(random.randint(0, 9))  # 數字
        lower = chr(random.randint(97, 122))  # 小寫字母
        upper = chr(random.randint(65, 90))  # 大寫字母
        c = random.choice([num, lower, upper])  # 隨機選取一個
        code += str(c) # 用畫筆把驗證碼畫到圖片上
        # 參數:xy:座標,畫在哪一個位置;text:畫的內容;fill:畫什麼顏色;font:字體格式
        draw_obj.text((35 + i*30, 0), c, fill=random_color(), font=font_obj) # 把圖片裏面的驗證碼的內容寫到session,且忽略大小寫
    request.session['v_code'] = code.upper() # 把圖片寫到內存
    from io import BytesIO f1 = BytesIO()  # 相似於文件的文件句柄:f1 = open()
    # 把圖片保存到內存
    img_obj.save(f1, format="PNG") # 從內存中取數據
    img_data = f1.getvalue() return HttpResponse(img_data, content_type='image/png') 4. 登陸視圖函數 def login(request): err_msg = ''
    if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') v_code = request.POST.get('v_code', '').upper() if v_code == request.session.get('v_code'): obj = auth.authenticate(request, username=username, password=password) if obj: auth.login(request, obj) # 認證成功 初始化權限信息
                ret = init_permission(request, obj) if ret: return ret return redirect(reverse('my_customer')) err_msg = '用戶名或密碼錯誤'
        else: err_msg = '驗證碼錯誤'
    
    return render(request, 'login.html', {'err_msg': err_msg})

 

四、驗證碼的額外小知識

畫完驗證碼後,能夠添加一些干擾 就是在 draw_obj.text((35 + i*30, 0), c, fill=random_color(), font=font_obj)以後加 1. 加干擾線 width = 250  # 圖片寬度(防止越界)
height = 35
for i in range(5): x1 = random.randint(0, width) x2 = random.randint(0, width) y1 = random.randint(0, height) y2 = random.randint(0, height) draw_obj.line((x1, y1, x2, y2), fill=random_color()) 2. 加干擾點 for i in range(40): draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=random_color()) x = random.randint(0, width) y = random.randint(0, height) draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=random_color())
相關文章
相關標籤/搜索