Django路由映射FBV 和 CBV
django中請求處理方式有2種:FBV(function base views) 和 CBV(class base views),換言之就是一種用函數處理請求,一種用類處理請求。javascript
FBVcss
# url.py from django.conf.urls import url, include from mytest import views urlpatterns = [ url(r‘^index/‘, views.index), ] # views.py from django.shortcuts import render def index(req): if req.method == ‘POST‘: print(‘method is :‘ + req.method) elif req.method == ‘GET‘: print(‘method is :‘ + req.method) return render(req, ‘index.html‘)
CBVhtml
# urls.py from mytest import views urlpatterns = [ # url(r‘^index/‘, views.index), url(r‘^index/‘, views.Index.as_view()), ] # views.py from django.views import View class Index(View): def get(self, req): print(‘method is :‘ + req.method) return render(req, ‘index.html‘) def post(self, req): print(‘method is :‘ + req.method) return render(req, ‘index.html‘) # 注:類要繼承 View ,類中函數名必須小寫。
關於CBV模式下的一個拓展java
# cbv 模式下繼承了django的view類 # 在請求來臨的時候,會調用繼承類的 dispatch 方法 # 經過反射的方法它會去調用本身寫的視圖函數, 那麼這即是一個切入點,能夠在本身的 cbv 視圖中,重寫這個方法。 class View(object): def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
分頁
1、Django內置分頁
from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger L = [] for i in range(999): L.append(i) # 模擬數據庫 生成數據 def index(request): current_page = request.GET.get('page') # 經過get請求獲得當前請求的頁數 paginator = Paginator(L, 10) # 實例化傳入倆個參數(全部數據,當頁顯示條數) # per_page: 每頁顯示條目數量 # count: 數據總個數 # num_pages:總頁數 # page_range:總頁數的索引範圍,如: (1,10),(1,200) # page: page對象 try: posts = paginator.page(current_page)# 傳入當前頁碼,觀源碼可得實例化了一個Page對象 # has_next 是否有下一頁 # next_page_number 下一頁頁碼 # has_previous 是否有上一頁 # previous_page_number 上一頁頁碼 # object_list 分頁以後的數據列表 # number 當前頁 # paginator paginator對象 except PageNotAnInteger: # 不是數字 posts = paginator.page(1) except EmptyPage: # 超出頁碼範圍 posts = paginator.page(paginator.num_pages) return render(request, 'index.html', {'posts': posts}) # posts封裝了一些方法
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <ul> {% for item in posts %} <li>{{ item }}</li> {% endfor %} </ul> <div class="pagination"> <span class="step-links"> {% if posts.has_previous %} <a href="?p={{ posts.previous_page_number }}">上一頁</a> {% endif %} <span class="current"> Page {{ posts.number }} of {{ posts.paginator.num_pages }}. </span> {% if posts.has_next %} <a href="?p={{ posts.next_page_number }}">下一頁</a> {% endif %} </span> </div> </body> </html>
那麼、Django的內置分頁基本倆個類實現、並封裝了一些方法來使用、此時並不能知足有一些的需求python
2、Django內置分頁的拓展
from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger user = [] for i in range(1, 1001): dic = {'name': 'root' + str(i), 'pwd': i} user.append(dic) class DiyPaginator(Paginator): def __init__(self, current_page,max_pager_num, *args, **kwargs): """ :param current_page: 當前頁碼 :param max_pager_num: 顯示頁碼個數的最大值 :param args: :param kwargs: """ self.current_page = int(current_page) self.max_pager_num = int(max_pager_num) super(DiyPaginator,self).__init__(*args,**kwargs) def pager_num_range(self): # 須要的參數 # 當前頁碼 self.current_page # 頁碼數量 self.max_pager_num # 總頁數 self.num_pages # 若是總頁數小於頁碼個數最大值的狀況 if self.num_pages < self.max_pager_num: return range(1,self.num_pages+1) # 返回 從 1 到 總頁數 # 若是總頁數大於頁碼數量且當前所選頁碼小於頁碼數量的一半 part = self.max_pager_num//2 if self.current_page <= part: return range(1,self.max_pager_num+1) # 返回 從 1 到 頁碼個數最大值 # 若是當前頁碼加一半的頁碼 大於 總頁數 if (self.current_page+part) > self.num_pages: # 返回 從總頁數-最大頁碼數 到 總頁數 range的用法在此不做解釋 # 例如 96頁+5頁 超出總頁數 則返回的範圍是 從 總頁數-最大頁碼數量+1 到 總頁數+1 return range(self.num_pages-self.max_pager_num+1,self.num_pages+1) # 其他狀況從 當前頁碼減去顯示頁碼的平均值開始 到 當前頁碼加顯示頁碼的平均值(並加一)結束 return range(self.current_page-part,self.current_page+part+1) def index(request): p = request.GET.get('page') start = (int(p)-1)*10 end = int(p)*10 data = user[start:end] return render(request,'index.html',{'data':data,'user':user}) def index1(request): current_page = request.GET.get('page') paginator = DiyPaginator(current_page, 9, user, 10) # Paginator所封裝的方法 # per_page: 每頁顯示條目數量 # count: 數據總個數 # num_pages:總頁數 # page_range:總頁數的索引範圍,如: (1,10),(1,200) # page: page對象 try: posts = paginator.page(current_page) # has_next 是否有下一頁 # next_page_number 下一頁頁碼 # has_previous 是否有上一頁 # previous_page_number 上一頁頁碼 # object_list 分頁以後的數據列表 # number 當前頁 # paginator paginator對象 except PageNotAnInteger: # 不是整形數字 posts = paginator.page(1) except EmptyPage: # 若是是空值 posts = paginator.page(paginator.num_pages) return render(request,'index1.html',{'posts':posts})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for row in posts.object_list %} <li>{{ row.name }}-{{ row.pwd }}</li> {% endfor %} </ul> {% include 'include/pager.html' %} </body> </html> ################################## include 組件代碼 {% if posts.has_previous %} <a href="/index1?page={{ posts.previous_page_number }}">上一頁</a> {% endif %} {% for num in posts.paginator.pager_num_range %} {% if num == posts.number %} <a style="color: red;font-size: 20px" href="/index1?page={{ num }}">{{ num }}</a> {% else %} <a href="/index1?page={{ num }}">{{ num }}</a> {% endif %} {% endfor %} {% if posts.has_next %} <a href="/index1?page={{ posts.next_page_number }}">下一頁</a> {% endif %} <span> 當前頁:{{ posts.number }} 總頁數:{{ posts.paginator.num_pages }} </span>
3、自定義分頁(適用於任何地方)
- 建立處理分頁數據的類
- 根據分頁數據獲取數據
- 輸出分頁HTML,即:[上一頁][1][2][3][4][5][下一頁]或者額外的做出一些拓展也能夠
建立處理分頁數據的類時,大體也須要四個參數(詳情觀看類構造方法)jquery
一、爲了減小服務器內存的負載,再也不獲取全部數據的,而是得到全部數據的總個數,而後再根據索引查數據庫的內容數據庫
二、當前頁碼django
三、每頁顯示的行數json
四、頁碼顯示的數量bootstrap
對於頁數的顯示大體也能夠歸類爲三種狀況(詳情觀看類中page_num_range函數)
一、計算的總頁數小於頁碼顯示的數量
二、計算的總頁數大於頁碼顯示的數量
A、當前頁數小於頁碼數量的一半
B、當前頁數加頁碼數量的一半超出總頁數的範圍
三、正常狀況
從 當前頁數 減 一半頁碼數量 到 當前頁數 加 一半頁碼數量
class Pagination(object): def __init__(self,totalCount,currentPage,perPageItemNum=10,maxPageNum=9): """ :param totalCount: 全部數據總個數 :param currentPage: 當前頁數 :param perPageItemNum: 每頁顯示行數 :param maxPageNum: 最多顯示頁碼個數 """ self.total_count = totalCount # 對當前的頁碼進行一次異常捕獲 try: currentPage = int(currentPage) if currentPage <= 0: currentPage = 1 self.current_page = currentPage except Exception: self.current_page = 1 self.per_page_item_num = perPageItemNum self.max_page_num = maxPageNum @property def start(self): # 數據索引開始的值 return (self.current_page-1) * self.per_page_item_num @property def end(self): # 數據索引結束的值 return self.current_page * self.per_page_item_num @property def num_pages(self): """ 總頁數 :return: """ # 得商取餘得內置函數 x, o = divmod(self.total_count,self.per_page_item_num) if o == 0: return x return x + 1 @property def page_num_range(self): if self.num_pages < self.max_page_num: return range(1, self.num_pages+1) part = self.max_page_num//2 if self.current_page <= part: return range(1,self.max_page_num+1) if (self.current_page+part) > self.num_pages: return range(self.num_pages-self.max_page_num+1, self.num_pages+1) return range(self.current_page-part, self.current_page+part+1) def page_str(self): page_list = [] first = "<li><a href='/index2/?page=1'>首頁</a></li>" page_list.append(first) if self.current_page == 1: prev_page = "<li><a href='#'>上一頁</a></li>" else: prev_page = "<li><a href='/index2/?page=%s'>上一頁</a></li>" %(self.current_page-1) page_list.append(prev_page) for i in self.page_num_range: if i == self.current_page: temp = "<li class='active'><a href='/index2/?page=%s'>%s</a></li>" %(i,i) else: temp = "<li><a href='/index2/?page=%s'>%s</a></li>" % (i, i) page_list.append(temp) if self.current_page == self.num_pages: next_page = "<li><a href='#'>下一頁</a></li>" else: next_page = "<li><a href='/index2/?page=%s'>下一頁</a></li>" %(self.current_page+1) page_list.append(next_page) last = "<li><a href='/index2/?page=%s'>尾頁</a></li>" %self.num_pages page_list.append(last) return ''.join(page_list)
def index2(request): from page.diypage import Pagination current_page = request.GET.get('page') page_obj = Pagination(1000,current_page) data_list = user[page_obj.start:page_obj.end] return render(request,'index2.html',{ 'data' : data_list, 'page_obj' : page_obj })
# 本頁面引用了bootstrap樣式 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css" /> </head> <body> <ul> {% for row in data %} <li>{{ row.name }}-{{ row.pwd }}</li> {% endfor %} </ul> {% for i in page_obj.pager_num_range %} <a href="/index2/?page={{ i }}">{{ i }}</a> {% endfor %} <hr/> <ul class="pagination pagination-sm"> {{ page_obj.page_str|safe }} </ul> <div style="height: 300px;"></div> </body> </html>
序列化
序列化是將對象狀態轉換爲可保持或傳輸的格式的過程
反序列化是指將存儲在存儲媒體中的對象狀態裝換成對象的過程
例如遊戲都有存檔的功能、再次開始的時候只需讀檔便可(這便是一個序列化與反序列的過程)
序列化也能夠將一個對象傳遞到另外一個地方的
關於Django中的序列化主要應用在將數據庫中檢索的數據返回給客戶端用戶,特別的Ajax請求通常返回的爲Json格式。
一、serializers
from django.core import serializers ret = models.BookType.objects.all() data = serializers.serialize("json", ret)
二、json.dumps
import json #ret = models.BookType.objects.all().values('caption') ret = models.BookType.objects.all().values_list('caption') ret=list(ret) result = json.dumps(ret)
三、from django.http import JsonResponse
django JsonResponse不支持返回列表形式的序列化。例如看看這個類的構造方法是怎麼樣執行的...
class JsonResponse(HttpResponse): """ 將數據序列化成爲JSON的Http響應類 :param data: Data to be dumped into json. By default only ``dict`` objects are allowed to be passed due to a security flaw before EcmaScript 5. See the ``safe`` parameter for more information. :param encoder: Should be an json encoder class. Defaults to ``django.core.serializers.json.DjangoJSONEncoder``. :param safe: Controls if only ``dict`` objects may be serialized. Defaults to ``True``. :param json_dumps_params: A dictionary of kwargs passed to json.dumps(). """ def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs): # 若是safe爲True 和 data 不是 dict的實例對象 則拋出異常 if safe and not isinstance(data, dict): raise TypeError( 'In order to allow non-dict objects to be serialized set the ' 'safe parameter to False.' ) if json_dumps_params is None: json_dumps_params = {} kwargs.setdefault('content_type', 'application/json') data = json.dumps(data, cls=encoder, **json_dumps_params) super(JsonResponse, self).__init__(content=data, **kwargs)
Cookices
Cookie是存儲在用戶瀏覽器上的一個鍵值對
A、獲取Cookies
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 參數: default: 默認值 salt: 加密鹽 max_age: 後臺控制過時時間
B、設置Cookies
rep = HttpResponse(...) 或 rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽',...) 參數: key, 鍵 value='', 值 max_age=None, 超時時間 expires=None, 超時時間(IE requires expires, so set it if hasn't been already.) path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:跟路徑的cookie能夠被任何url的頁面訪問 domain=None, Cookie生效的域名 secure=False, https傳輸 httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)
C、因爲Cookies保存在客戶端的電腦上,因此,JavaScript和jquery也能夠操做cookie.
更多詳情更新中...
Session
Session是存儲在服務器的一組鍵值對,且它依賴於Cookie,且安全係數比Cookie高
Django中默認支持Session,其內部提供了5種類型的Session供開發者使用:
- 數據庫(默認)
- 緩存
- 文件
- 緩存+數據庫
- 加密cookie
A、數據庫Session
Django默認支持Session,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑(默認) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默認) SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie(默認) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸(默認) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默認) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過時(默認) SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改以後才保存(默認) b. 使用 def index(request): # 獲取、設置、刪除Session中數據 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在則不設置 del request.session['k1'] # 全部 鍵、值、鍵值對 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用戶session的隨機字符串 request.session.session_key # 將全部Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否 request.session.exists("session_key") # 刪除當前用戶的全部Session數據 request.session.delete("session_key") request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。 * 若是value是個datatime或timedelta,session就會在這個時間後失效。 * 若是value是0,用戶關閉瀏覽器session就會失效。 * 若是value是None,session會依賴全局session失效策略。
跨站請求僞造
Django爲用戶實現防止跨站請求僞造的功能,經過中間件 django.middleware.csrf.CsrfViewMiddleware 來完成。而對於django中設置防跨站請求僞造功能有分爲全局和局部。
全局:
中間件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置全局中間件。
- @csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
Django中間件
django 中的中間件(middleware),在django中,中間件其實就是一個類,在請求到來和結束後,django會根據本身的規則在合適的時機執行中間件中相應的方法。
在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每個元素就是一箇中間件,以下圖。
例如,能夠在項目中的任何地方建立一個middleware.py的文件(可隨意建立, 註冊中間件的時候配置好路徑便可)
中間件中能夠定義四個方法,分別是:
- process_request(self,request)
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_template_response(self,request,response)
- process_exception(self, request, exception)
- process_response(self, request, response)
以上方法的返回值能夠是None和HttpResonse對象,若是是None,則繼續按照django定義的規則向下執行,若是是HttpResonse對象,則直接將該對象返回給用戶。
自定義中間件
一、建立中間件
class CustomMiddleware(object): def process_request(self,request): pass def process_view(self, request, callback, callback_args, callback_kwargs): i =1 def process_exception(self, request, exception): pass def process_response(self, request, response): return response
二、註冊中間件
如第一圖所示最後一條,可根據自身業務去自定義一些中間件操做。
Django信號
1,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 # 建立數據庫鏈接時,自動觸發
調用內置信號的倆種形式
2,django自定義信號
1 )、定義信號
全部的信號都是 `django.dispatch.Signal` 的實例、那麼在項目某個app下建立 名爲 `custom_signals.py` 的文件,以下這麼寫
# filename: custom_singals.py import django.dispatch # 聲明瞭一個 `pizza_done` 信號,它將爲接收者提供 `toppings`, `size` 參數 # 能夠在任什麼時候候更改這個參數列表 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
並配置信號回調函數
在項目某個app下建立 名爲 `register.py` 的文件,以下這麼寫
# filename: register.py # 註冊信號使用 from app01.custom_singals import pizza_done def pizza_done_func(sender, **kwargs): print("msg:", sender, kwargs) pizza_done.connect(pizza_done_func)
2 )、註冊信號
項目啓動的時候要將這些信號註冊,那麼在對應 `app` 的 `apps.py` 文件中以下
# filename: app_name/apps.py from django.apps import AppConfig # `django` 工具包中封裝好的動態導入模塊的方法 from django.utils.module_loading import import_module class App01Config(AppConfig): name = 'app01' def ready(self): # 項目啓動全部 `app` 的 `ready` 方法下的代碼均會執行 import_module("app01.register") # 註冊信號
3 )、發送信號
事先定義 一組 `url` 映射關係,在視圖業務操做中發送咱們的信號( 有倆種發送信號的方法 )
from django.shortcuts import HttpResponse from app01.custom_singals import pizza_done # Create your views here. class PizzaStore: def send_pizza(self, toppings, size): # 發送信號 pizza_done.send(sender=self.__class__, toppings=toppings, size=size) # "OR" 上下倆個方法是等價的, 區別 ~ ~ pizza_done.send_robust(sender=self.__class__, toppings=toppings, size=size) def index(request, ): # 業務操做, 執行信號發送操做 PizzaStore().send_pizza("chicken", "40") return HttpResponse("ok")
Django自定義manage.py命令
正如以前,已經所知一些 `Django` 的一些命令, 例如 runserver、migrate....,那麼它們執行的機制是如何的呢,是否能夠作一些其它的命令知足咱們的一些需求呢,請看下文
在 APP 建立相關目錄路徑,`closepoll.py` 即是自定義的命令文件
polls/ __init__.py models.py management/ __init__.py commands/ __init__.py closepoll.py tests.py views.py
文件裏面必須繼承基類,並實現抽象方法 `handle`
from django.core.management.base import BaseCommand, CommandError from polls.models import Question as Poll class Command(BaseCommand): help = 'Closes the specified poll for voting' def add_arguments(self, parser): # 添加自定義參數,例如 python manage.py runserver -h 0.0.0.0 -p 8080 這樣子 ~~ parser.add_argument('poll_id', nargs='+', type=int) def handle(self, *args, **options): for poll_id in options['poll_id']: try: poll = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise CommandError('Poll "%s" does not exist' % poll_id) poll.opened = False poll.save() self.stdout.write(self.style.SUCCESS('Successfully closed poll "%s"' % poll_id))
運行自定義命令
python3 manage.py closepoll -poll_id 10
# 上述是在命令行運行文件,那麼在代碼中如何運行,
from django.core import management
management.call_command('closepoll', args=()) # 參數詳解待更新