Django中間件css
咱們從瀏覽器發出一個請求 Request,獲得一個響應後的內容 HttpResponse ,這個請求傳遞到 Django的過程以下:html
也就是說,每個請求都是先經過中間件中的 process_request 函數,這個函數返回 None 或者 HttpResponse 對象,若是返回前者,繼續處理其它中間件,若是返回一個 HttpResponse,就處理停止,返回到網頁上。python
中間件不用繼承自任何類(能夠繼承 object ),下面一箇中間件大概的樣子:web
class CommonMiddleware(object): def process_request(self, request): return None def process_response(self, request, response): return response
還有process_view,process_exception和process_template_response函數。算法
1、好比咱們要作一個攔截器,發生有惡意訪問網站的人,就攔截它!數據庫
假如咱們經過一種技術,好比統計一分鐘訪問頁面數,太多酒把他的ip加入到黑名單BLOCKED_IPS(這部分沒有提供代碼,主要講中間件部分)django
#項目 wulaoer 文件名 wulaoer/middleware.py class BlockedIpMiddleware(object): def process_request(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLOCKED_IPS", []): return http.HttpResponseForbidden('<h1>Forbidden</h1>')
這裏的代碼的功能就是獲取當前訪問者的IP(request.META['REMOTE_ADDR']),若是這個IP在黑名單中就攔截,若是不在就返回None(函數中沒有返回值其實就是默認爲None),把這個中間件的Python路徑寫到settings.py中編程
MIDDLEWARE_CLASSES = ( 'wulaoer.middleware.BlockedIpMiddleware', ...其它的中間件 )
Django會從MIDDLEWARE_CLASSES中按照從上到下到順序一個個執行中間件中的process_request函數,而其中process_response函數則是最前面的最後執行。json
2、在好比,咱們在網站放到服務器上正式運行後,DEBUG改成了False ,這樣更安全,可是有時候發生錯誤不能顯示錯誤詳情頁面,有沒有辦法處理好這兩個事情呢?瀏覽器
一、普通訪問者看到的是友好的報錯信息
二、管理員看到的是錯誤詳情,以便修復BUG
固然能夠有,利用中間件就能夠作到!代碼以下:
import sys from django.views.debug import technical_500_response from django.conf import settings class UserBasedExceptionMiddleware(object): def process_exception(self, request, exception): if request.user.is_superuser or request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: return technical_500_response(request, *sys.exc_info())
把這個中間件像上面同樣,加到你的 settings.py 中的 MIDDLEWARE_CLASSES 中,能夠放到最後,這樣能夠看到其它中間件的 process_request的錯誤。
當訪問者爲管理員時,就給出錯誤詳情,好比訪問本站的不存在的頁面:http://localhost/admin/
普通人看到的是普通的 404(本身點開看看),而我能夠看到:
三,分享一個簡單的識別手機的中間件,更詳細的能夠參考這個:django-mobi 或 django-mobile
MOBILE_USERAGENTS = ("2.0 MMP","240x320","400X240","AvantGo","BlackBerry", "Blazer","Cellphone","Danger","DoCoMo","Elaine/3.0","EudoraWeb", "Googlebot-Mobile","hiptop","IEMobile","KYOCERA/WX310K","LG/U990", "MIDP-2.","MMEF20","MOT-V","NetFront","Newt","Nintendo Wii","Nitro", "Nokia","Opera Mini","Palm","PlayStation Portable","portalmmm","Proxinet", "ProxiNet","SHARP-TQ-GX10","SHG-i900","Small","SonyEricsson","Symbian OS", "SymbianOS","TS21i-10","UP.Browser","UP.Link","webOS","Windows CE", "WinWAP","YahooSeeker/M1A1-R2D2","iPhone","iPod","Android", "BlackBerry9530","LG-TU915 Obigo","LGE VX","webOS","Nokia5800") class MobileTemplate(object): """ If a mobile user agent is detected, inspect the default args for the view func, and if a template name is found assume it is the template arg and attempt to load a mobile template based on the original template name. """ def process_view(self, request, view_func, view_args, view_kwargs): if any(ua for ua in MOBILE_USERAGENTS if ua in request.META["HTTP_USER_AGENT"]): template = view_kwargs.get("template") if template is None: for default in view_func.func_defaults: if str(default).endswith(".html"): template = default if template is not None: template = template.rsplit(".html", 1)[0] + ".mobile.html" try: get_template(template) except TemplateDoesNotExist: pass else: view_kwargs["template"] = template return view_func(request, *view_args, **view_kwargs) return None
參考文檔:https://docs.djangoproject.com/en/1.8/topics/http/middleware/
Python/Django微信接口
填寫相應的網址,Token(令牌) 是隨便寫的,你本身想寫什麼就寫什麼,微信驗證時檢驗是否寫的和服務器上的TOKEN同樣,同樣則經過。
關注一下吳老二的微信號吧,能夠隨時隨地查閱教程哦,體驗一下自強學堂的微信的各類功能再閱讀效果更佳!
本身動手寫微信的驗證: views.py
#coding=utf-8 import hashlib import json from lxml import etree from django.utils.encoding import smart_str from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse from auto_reply.views import auto_reply_main # 修改這裏 WEIXIN_TOKEN = 'write-a-value' @csrf_exempt def weixin_main(request): """ 全部的消息都會先進入這個函數進行處理,函數包含兩個功能, 微信接入驗證是GET方法, 微信正常的收發消息是用POST方法。 """ if request.method == "GET": signature = request.GET.get("signature", None) timestamp = request.GET.get("timestamp", None) nonce = request.GET.get("nonce", None) echostr = request.GET.get("echostr", None) token = WEIXIN_TOKEN tmp_list = [token, timestamp, nonce] tmp_list.sort() tmp_str = "%s%s%s" % tuple(tmp_list) tmp_str = hashlib.sha1(tmp_str).hexdigest() if tmp_str == signature: return HttpResponse(echostr) else: return HttpResponse("weixin index") else: xml_str = smart_str(request.body) request_xml = etree.fromstring(xml_str) response_xml = auto_reply_main(request_xml)# 修改這裏 return HttpResponse(response_xml)
auto_reply_main 是用來處理消息,回覆消息的,須要本身進一步完善。
使用第三方包實現:
關於Django開發微信,有已經作好的如今的包可使用 wechat_sdk 這個包,使用文檔 也比較完善,可是在處理加密一部分沒有作,在微信公衆平臺上,須要用明文驗證,若是要加密,本身參照微信官網的加密算法。
使用 wechat_sdk 的例子(吳老二微信號簡化後的例子):
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.http.response import HttpResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt from wechat_sdk import WechatBasic from wechat_sdk.exceptions import ParseError from wechat_sdk.messages import TextMessage WECHAT_TOKEN = 'zqxt' AppID = '' AppSecret = '' # 實例化 WechatBasic wechat_instance = WechatBasic( token=WECHAT_TOKEN, appid=AppID, appsecret=AppSecret ) @csrf_exempt def index(request): if request.method == 'GET': # 檢驗合法性 # 從 request 中提取基本信息 (signature, timestamp, nonce, xml) signature = request.GET.get('signature') timestamp = request.GET.get('timestamp') nonce = request.GET.get('nonce') if not wechat_instance.check_signature( signature=signature, timestamp=timestamp, nonce=nonce): return HttpResponseBadRequest('Verify Failed') return HttpResponse( request.GET.get('echostr', ''), content_type="text/plain") # 解析本次請求的 XML 數據 try: wechat_instance.parse_data(data=request.body) except ParseError: return HttpResponseBadRequest('Invalid XML Data') # 獲取解析好的微信請求信息 message = wechat_instance.get_message() # 關注事件以及不匹配時的默認回覆 response = wechat_instance.response_text( content = ( '感謝您的關注!\n回覆【功能】兩個字查看支持的功能,還能夠回覆任意內容開始聊天' '\n【<a href="http://www.ziqiangxuetang.com">自強學堂手機版</a>】' )) if isinstance(message, TextMessage): # 當前會話內容 content = message.content.strip() if content == '功能': reply_text = ( '目前支持的功能:\n1. 關鍵詞後面加上【教程】兩個字能夠搜索教程,' '好比回覆 "Django 後臺教程"\n' '2. 回覆任意詞語,查天氣,陪聊天,講故事,無所不能!\n' '還有更多功能正在開發中哦 ^_^\n' '【<a href="http://www.ziqiangxuetang.com">自強學堂手機版</a>】' ) elif content.endswith('教程'): reply_text = '您要找的教程以下:' response = wechat_instance.response_text(content=reply_text) return HttpResponse(response, content_type="application/xml")
下面是一個更詳細複雜的使用例子:
models.py
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class KeyWord(models.Model): keyword = models.CharField( '關鍵詞', max_length=256, primary_key=True, help_text='用戶發出的關鍵詞') content = models.TextField( '內容', null=True, blank=True, help_text='回覆給用戶的內容') pub_date = models.DateTimeField('發表時間', auto_now_add=True) update_time = models.DateTimeField('更新時間', auto_now=True, null=True) published = models.BooleanField('發佈狀態', default=True) def __unicode__(self): return self.keyword class Meta: verbose_name='關鍵詞' verbose_name_plural=verbose_name
views.py
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.http.response import HttpResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt from wechat_sdk import WechatBasic from wechat_sdk.exceptions import ParseError from wechat_sdk.messages import (TextMessage, VoiceMessage, ImageMessage, VideoMessage, LinkMessage, LocationMessage, EventMessage ) from wechat_sdk.context.framework.django import DatabaseContextStore from .models import KeyWord as KeyWordModel # 實例化 WechatBasic wechat_instance = WechatBasic( token='zqxt', appid='xx', appsecret='xx' ) @csrf_exempt def index(request): if request.method == 'GET': # 檢驗合法性 # 從 request 中提取基本信息 (signature, timestamp, nonce, xml) signature = request.GET.get('signature') timestamp = request.GET.get('timestamp') nonce = request.GET.get('nonce') if not wechat_instance.check_signature( signature=signature, timestamp=timestamp, nonce=nonce): return HttpResponseBadRequest('Verify Failed') return HttpResponse( request.GET.get('echostr', ''), content_type="text/plain") # POST # 解析本次請求的 XML 數據 try: wechat_instance.parse_data(data=request.body) except ParseError: return HttpResponseBadRequest('Invalid XML Data') # 獲取解析好的微信請求信息 message = wechat_instance.get_message() # 利用本次請求中的用戶OpenID來初始化上下文對話 context = DatabaseContextStore(openid=message.source) response = None if isinstance(message, TextMessage): step = context.get('step', 1) # 當前對話次數,若是沒有則返回 1 # last_text = context.get('last_text') # 上次對話內容 content = message.content.strip() # 當前會話內容 if message.content == '新聞': response = wechat_instance.response_news([ { 'title': '自強學堂', 'picurl': 'http://www.ziqiangxuetang.com/static/images/newlogo.png', 'description': '自強學堂致力於提供優質的IT技術教程, 網頁製做,服務器後臺編寫,以及編程語言,如HTML,JS,Bootstrap,Python,Django。同時也提供大量在線實例,經過實例,學習更容易,更輕鬆。', 'url': 'http://www.ziqiangxuetang.com', }, { 'title': '百度', 'picurl': 'http://doraemonext.oss-cn-hangzhou.aliyuncs.com/test/wechat-test.jpg', 'url': 'http://www.baidu.com', }, { 'title': 'Django 教程', 'picurl': 'http://www.ziqiangxuetang.com/media/uploads/images/django_logo_20140508_061519_35.jpg', 'url': 'http://www.ziqiangxuetang.com/django/django-tutorial.html', } ]) return HttpResponse(response, content_type="application/xml") else: try: keyword_object = KeyWordModel.objects.get(keyword=content) reply_text = keyword_object.content except KeyWordModel.DoesNotExist: try: reply_text = KeyWordModel.objects.get(keyword='提示').content except KeyWordModel.DoesNotExist: reply_text = ('/:P-(好委屈,數據庫翻個遍也沒找到你輸的關鍵詞!\n' '試試下面這些關鍵詞吧:\nKEYWORD_LIST\n' '<a href="http://www.rxnfinder.org">RxnFinder</a>' '感謝您的支持!/:rose' ) # 將新的數據存入上下文對話中 context['step'] = step + 1 context['last_text'] = content context.save() # 很是重要!請勿忘記!臨時數據保存入數據庫! if 'KEYWORD_LIST' in reply_text: keyword_objects = KeyWordModel.objects.exclude(keyword__in=[ '關注事件', '測試', 'test', '提示']).filter(published=True) keywords = ('{}. {}'.format(str(i), k.keyword) for i, k in enumerate(keyword_objects, 1)) reply_text = reply_text.replace( 'KEYWORD_LIST', '\n'.join(keywords)) # 文本消息結束 elif isinstance(message, VoiceMessage): reply_text = '語音信息我聽不懂/:P-(/:P-(/:P-(' elif isinstance(message, ImageMessage): reply_text = '圖片信息我也看不懂/:P-(/:P-(/:P-(' elif isinstance(message, VideoMessage): reply_text = '視頻我不會看/:P-(' elif isinstance(message, LinkMessage): reply_text = '連接信息' elif isinstance(message, LocationMessage): reply_text = '地理位置信息' elif isinstance(message, EventMessage): # 事件信息 if message.type == 'subscribe': # 關注事件(包括普通關注事件和掃描二維碼形成的關注事件) follow_event = KeyWordModel.objects.get(keyword='關注事件') reply_text = follow_event.content # 若是 key 和 ticket 均不爲空,則是掃描二維碼形成的關注事件 if message.key and message.ticket: reply_text += '\n來源:掃描二維碼關注' else: reply_text += '\n來源:搜索名稱關注' elif message.type == 'unsubscribe': reply_text = '取消關注事件' elif message.type == 'scan': reply_text = '已關注用戶掃描二維碼!' elif message.type == 'location': reply_text = '上報地理位置' elif message.type == 'click': reply_text = '自定義菜單點擊' elif message.type == 'view': reply_text = '自定義菜單跳轉連接' elif message.type == 'templatesendjobfinish': reply_text = '模板消息' response = wechat_instance.response_text(content=reply_text) return HttpResponse(response, content_type="application/xml")
Django單元測試
Django一系列教程,前面的例子都是咱們寫好代碼後,運行開發服務器,在瀏覽器上本身點擊測試,看寫的代碼是否正常,可是這樣作很麻煩,由於之後若是有改動,可能會影響之前原本正常的功能,這樣之前的功能又得測試一遍,很是不方便,Django中有完善的單元測試,咱們能夠對開發的每個功能進行單元測試,這樣只要運行一個命令 python manage.py test,就能夠測試功能是否正常。
一言以蔽之,測試就是檢查代碼是否按照本身的預期那樣運行。
測試驅動開發: 有時候,咱們知道本身須要的功能(結果),並不知道代碼如何書寫,這時候就能夠利用測試驅動開發(Test Driven Development),先寫出咱們期待獲得的結果(把測試代碼先寫出來),再去完善代碼,直到不報錯,咱們就完成了。
《改善Python的91個建議》一書中說:單元測試毫不是浪費時間的無用功,它是高質量代碼的保障之一,在軟件開發的一節中值得投入精力和時間去把好這一關。
1. Python 中 單元測試簡介:
下面是一個 Python的單元測試簡單的例子:
假如咱們開發一個除法的功能,有的同窗可能以爲很簡單,代碼是這樣的:
def division_funtion(x, y): return x / y
可是這樣寫究竟對仍是不對呢,有些同窗能夠在代碼下面這樣測試:
def division_funtion(x, y): return x / y if __name__ == '__main__': print division_funtion(2, 1) print division_funtion(2, 4) print division_funtion(8, 3)
可是這樣運行後獲得的結果,本身每次都得算一下去核對一遍,很不方便,Python中有 unittest 模塊,能夠很方便地進行測試,詳情能夠文章最後的連接,看官網文檔的詳細介紹。
下面是一個簡單的示例:
import unittest def division_funtion(x, y): return x / y class TestDivision(unittest.TestCase): def test_int(self): self.assertEqual(division_funtion(9, 3), 3) def test_int2(self): self.assertEqual(division_funtion(9, 4), 2.25) def test_float(self): self.assertEqual(division_funtion(4.2, 3), 1.4) if __name__ == '__main__': unittest.main()
我簡單地寫了三個測試示例(不必定全面,只是示範,好比沒有考慮除數是0的狀況),運行後發現:
F.F ====================================================================== FAIL: test_float (__main__.TestDivision) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/tu/YunPan/mydivision.py", line 16, in test_float self.assertEqual(division_funtion(4.2, 3), 1.4) AssertionError: 1.4000000000000001 != 1.4 ====================================================================== FAIL: test_int2 (__main__.TestDivision) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/tu/YunPan/1.py", line 13, in test_int2 self.assertEqual(division_funtion(9, 4), 2.25) AssertionError: 2 != 2.25 ---------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (failures=2)
汗!發現了沒,居然兩個都失敗了,測試發現:
4.2除以3 等於 1.4000000000000001 不等於指望值 1.4
9除以4等於2,不等於指望的 2.25
下面咱們就是要修復這些問題,再次運行測試,直到運行不報錯爲止。
譬如根據實際狀況,假設咱們只須要保留到小數點後6位,能夠這樣改:
def division_funtion(x, y): return round(float(x) / y, 6)
再次運行就不報錯了:
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
Python 單元測試 官方文檔:
Python 2 (https://docs.python.org/2/library/unittest.html)
Python 3 (https://docs.python.org/3/library/unittest.html)
2. Django 中 單元測試:(不斷完善中,後期會增長對前面講解的內容的測試)
2.1 簡單測試例子:
from django.test import TestCase from myapp.models import Animal class AnimalTestCase(TestCase): def setUp(self): Animal.objects.create(name="lion", sound="roar") Animal.objects.create(name="cat", sound="meow") def test_animals_can_speak(self): """Animals that can speak are correctly identified""" lion = Animal.objects.get(name="lion") cat = Animal.objects.get(name="cat") self.assertEqual(lion.speak(), 'The lion says "roar"') self.assertEqual(cat.speak(), 'The cat says "meow"')
這個例子是測試myapp.models 中的 Animal 類相關的方法功能。
2.2 用代碼訪問網址的方法:
>>> from django.test import Client >>> c = Client() >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) >>> response.status_code 200 >>> response = c.get('/customer/details/') >>> response.content '<!DOCTYPE html...'
咱們能夠用 django.test.Client 的實例來實現 get 或 post 內容,檢查一個網址返回的網頁源代碼。
默認狀況下CSRF檢查是被禁用的,若是測試須要,能夠用下面的方法:
>>> from django.test import Client >>> csrf_client = Client(enforce_csrf_checks=True)
使用 csrf_client 這個實例進行請求便可。
指定瀏覽USER-AGENT:
>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
模擬post上傳附件:
from django.test import Client c = Client() with open('wishlist.doc') as fp: c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
測試網頁返回狀態:
from django.test import TestCase class SimpleTest(TestCase): def test_details(self): response = self.client.get('/customer/details/') self.assertEqual(response.status_code, 200) def test_index(self): response = self.client.get('/customer/index/') self.assertEqual(response.status_code, 200)
咱們用 self.client 便可,不用 client = Client() 這樣實例化,更方便,咱們還能夠繼承 Client,添加一些其它方法:
from django.test import TestCase, Client class MyTestClient(Client): # Specialized methods for your environment ... class MyTest(TestCase): client_class = MyTestClient def test_my_stuff(self): # Here self.client is an instance of MyTestClient... call_some_test_code()
定製 self.client 的方法:
from django.test import Client, TestCase class MyAppTests(TestCase): def setUp(self): super(MyAppTests, self).setUp() self.client = Client(enforce_csrf_checks=True) def test_home(self): response = self.client.get('/') self.assertEqual(response.status_code, 200)