緩存通俗來講:就是把數據先保存在某個地方,下次再讀取的時候不用再去原位置讀取,讓訪問速度更快。
緩存機制圖解html
1. 開發調試
2. 內存
3. 文件
4. 數據庫
5. Memcache緩存(python-memcached模塊)
6. Memcache緩存(pylibmc模塊)python
注意:下面對緩存進行配置,其實就是設置了緩存方式,是爲了設置緩存的存放位置,若是沒有設置緩存方式,那麼就默認使用的是本地內存緩存的方式。數據庫
配置緩存的方式 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 %}
關於Django中的序列化主要應用在將數據庫中檢索的數據返回給客戶端用戶,特別的Ajax請求通常返回的爲Json格式。json
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指定序列化的時候去執行這個類
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中,因此其會自動調用,而對於自定義信號則須要開發者在任意位置觸發。
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')
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())