1. 程式人生 > >(個人筆記)Django學習筆記整理

(個人筆記)Django學習筆記整理

‘@’ 為遺漏點或者難點
‘#’ 為重點標記
'若有打眼,歡迎自取,錯誤之處,還請指教

    DAY 11.24
    
   @ url統一資源定位符限定有2到4kb 因為瀏覽器位址列裡只能輸入這麼多東西
    
    1,MVT
    2,觀看中文文件
    3,學習重點 檢視 模型
    4,i課件回顧。
    5,新建專案,在pycharm裡新建專案 選擇django框架,選擇虛擬環境,
    workon meiduo  which python
    然後複製路徑拷貝到虛擬環境選項。
    6,專案檔案介紹。
    7,那啥是啥意思來著?就是需要定義路由規則?
    8,框架自帶伺服器和部署伺服器的區別。  自帶伺服器是什麼?
    9,setting是配置檔案的原因是在manage檔案中指定了配置命令,所以可以修改。
    10,建立應用,startapp  
              註冊/安裝 應用。
    11,contrib 提供的 
    12,試圖函式必須有接受引數,表示請求物件。
    13, 配置路由規則。from . import views
    當前包引入檢視模組
    14,url語法 ,第一個引數是爭著表示式,第二個引數是試圖模組裡的檢視函式名。
    @複習正則表示式
    abspath  絕對路徑
    host=【】放的是域名
    15,前後端分離不用csrf,交給網站實現
    16,root_urlconf 指定根級路由檔案 
    17, 模板檔案配置資訊 綁定了路徑資訊
    18, 指定wsgi
    19, 指定資料庫
    20,auth 使用者認證模組,做密碼驗證的 大概和註冊部分的方式有關。
    21,指定靜態檔案按請求目錄,改的時候要繫結檔案路目錄。
    @老師用的的啥輸入法?答 五筆
    22,路由規則:
    url字串:協議://域名:埠/路徑/?查詢字串
    匹配過程  拿到url 找到路徑部分 刪除最左邊的斜槓,與根級url進行按順序匹配。
    匹配失敗就返回404
    匹配成功 就刪除成功的部分,留下剩餘的部分如index/再與下一級的列表按順序匹配,失敗返回404 成功則返回結果。
    @與flask匹配區別,逐級匹配,不成功則不返回詳細對映。
    
    位置引數和關鍵子引數的區別,在與返回的引數是否是按位置排列,或者按關鍵字引數排列?
    
    
    @匹配級別 怎麼分的  根級包含的子級 有咩有會混淆的可能啊?感覺好亂啊
    23,django中設定路由規則不能以/開頭,推薦/結尾。
     
    
    24,從request中拿資料,四種途徑 就是四個資訊方面。
          24.1 路徑中:
    路徑中對應的部分加括號,並且要再函式中加引數提取回來。
    @怎麼知道別人給了幾個引數?也就是怎麼知道設定幾個變數來接收/?
    Da:肯定知道引數名,因為客戶端的請求是按前端定義好的引數格式傳送的,不是手動輸入的,是滑鼠點的。
    @正則表示式可以直接按位置再url裡命名。
    
    url(r'^a/([a-z]+)/(\d{4})/$', views.a),
    def a(request, city,year):
        print('city=%s' % city )
        print(year)
        return HttpResponse('ok')
    
    url(r'^b/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.b),
    def b(request, year, city):
        print(city)
        print(year)
        return HttpResponse("ok")
         24.2 查詢字串
    路徑後面以?開始 &連線的字典形式的引數就是查詢字串。
    獲取用GET.get 
    
    url('^get1/$',views.get1)
    
    def get1(request):
        dict1 = request.GET
        print(dict1)
        print(dict1.get('a'))
        return HttpResponse(dict1.get('a')
    
    
    怎麼獲取指定引數?肯定知道引數名,因為客戶端的請求是按前端定義好的引數格式傳送的,不是手動輸入的,是滑鼠點的。
        24.3 請求報文體 json等
    表單型別的資料:request.POST
    重要:只要請求體的資料是表單型別,無論是哪種請求方式(POST、PUT、PATCH、DELETE),都是使用request.POST來獲取請求體的表單資料
    非表單資料:request.body 返回json格式資料。
    @用postman模擬post a=10
    @json格式的資料構造必須是“”雙引號才能解析。是postman的問題。  獲取用request.body 並轉換資料。
    步驟為,1 接受byte型別資料 byte
                   2 轉換為字串dir =  byte .decode
                   3 (匯入json包)轉換為字典x'husn'husn'wei json.loads(dir)
        24.4 請求報文頭 mehtods
    request.屬性 常用的有
    request.method
    request.path
    request.user  當前使用者  登陸使用者或者遊客。
    @上面獲取引數的意思是 這些東西為以後寫介面做準備。
    25,返回jsonresponse格式資料 return JsonResponse({字典格式的資料})括號裡寫資料。
    如果不是json格式資料,設定字典的value值為  safe=False
    @其實括號裡有兩個引數  一個json資料 一個safe  預設true 所以不是字典就用false。
    @無論如何返回的是json格式的資料。
    
    def json1(request):
        # request.body===>接收請求體中的非表單資料,常用格式為json,型別為bytes
        str1 = request.body.decode()  # 轉字典串
        # 將字串轉字典
        # json.dumps()===>字典轉字串
        # json.loads()===>字串轉字典
        dict1 = json.loads(str1)
        print(dict1.get('a'))
        return HttpResponse('OK')
    
    ------狀態保持-----
    26,cookie 鍵值對方式存在瀏覽器中,
    寫法 response.set_cookie('','',max_age='')
    def cookie_set(request):
        response = HttpResponse('OK')
        response.set_cookie('a', 10, max_age=7 * 24 * 60 * 60)
        return response
    
    讀取 request.cookies.get('key')
    
    def cookie_get(request):
        a = request.COOKIES.get('a')
        print(a)
        return HttpResponse('OK')
    
    27,狀態保持之session
    儲存在redis裡  複製過來 必須有一項default
    儲存在快取中  再儲存到redis中。
    如果存成一樣的東西 記得手動分開。
    @將caches設定成動態模板!!
    寫法:request.session['hello']='django'
    
    def session_set(request):
        # request.session['a'] = 10
        request.session['b'] = 20
        return HttpResponse('OK')
    
    讀取:request.session.get(‘hello’)
    
    def session_get(request):
        print(request.session.get('a'))
        return HttpResponse('ok')
    
    刪除 1  :del request.session(''key)
    刪除2 : request.session.clear()
    刪除3: request.session.flush()
    def session_del(request):
        # del:將指定的鍵、值進行刪除
        # del request.session['b']
    
        # clear:將所有的鍵、值刪除
        # request.session.clear()
    
        # flush:刪除整條資料
        # request.session.flush()
    
    
    
    28,session加密儲存的資訊是將key和值整體加密存進redis中的,不是分開存的。
    @session預設過期時間是2周。
    @cookie預設過期時間是關閉瀏覽器。
    @session 依賴與cookie。
    
        return HttpResponse('OK')
    29,類檢視  實現請求方式不一樣的時候返回檢視不一樣。  並實現程式碼繼承和複用。
    
    class ShowView(View):
        def get(self, request):
            return HttpResponse('get')
    
        def post(self, request):
            return HttpResponse('post')
    
    
    def show(request):
        if request.method == 'GET':
            return HttpResponse('get')
        elif request.method == 'POST':
            return HttpResponse('post')
    
    」
    —————————
    
    DAY 11.25
    昨日總結:
    
    今日記錄:
    1,session 當作字典用就好了
    2,類檢視的as_view方法 是原始碼帶的
    他內部定義一個函式並返回了一個函式。所以路由規則裡寫的也是滿足規則的一個函式
    url('^.....view......as_view)
    3,dispatch 派遣排程。
    -----
    100 continue 繼續傳送
    101 switch 轉換協議
    
    200 ok
    
    301 moved permanently 重定向了
    
    400 badrequest 不能解析
    401 unauthorized  要求登陸
    403 forbidden 禁止訪問
    404 notfound 定向失敗
    405 methods notallowd 請求方法不允許
    406 not acceptable 客戶端無法接收
    410 請求頁面丟失
    
    
    500 internal server error 伺服器未知錯誤
    501 not lmplenmented 伺服器不支援請求
    502 bad gateway  伺服器閘道器無效
    504 gateway timeout 閘道器超時
    505 httpversion not supported 伺服器不支援請求的協議版本
    -------------
    4,getattr 獲得屬性
    類檢視 就是根據據請求方法找方法然後執行。
    
    5,類檢視使用裝飾器。再內部最先執行的函式錢加裝飾器。  這裡用的是dispatch()(也有get  post等已經執行的方法)
                 也可以匯入utils 匯入類裝飾包
    @method_decorator(wrapper1,name='dispatch')
    6,擴充套件功能類 以***Mixin結尾  表示功能類  用於特定的功能的封裝。 
    使用方法,寫個類繼承他一下。
    
    7,中介軟體 
     7.1 是什麼  相當於請求鉤子,為了給所有的試圖函式加上一定的功能。功能類似於一個裝飾器。比裝飾器更裝飾器!!中介軟體可以包含裝飾器。
    7.2 選擇標準,看裝飾的物件的多少。如果大部分需要裝飾,可以用中介軟體然後排除掉不用裝的檢視,就是做if判斷。
    7.3 定於語法與裝飾器相同
    7.4 註冊到setting中,middleware
    7.5 排除寫個if判斷
    7.6 中介軟體的執行順序:按照註冊順序,以試圖函式為分界,先進後出。
    
    8,模板 templates決定這麼檔案路徑的關鍵是已經在setting裡註冊了
    return裡面的引數:1 request, 2 xx.html 3,字典引數
    並在html裡接受字典的key進行渲染。
    @注意與jingja2裡不一樣的語法指出是 運算子左右各有一個空格。
    @過濾器引數冒號後面加  
    
    @複習 輸出直接{{ 變數 }}
                運算{% 函式 %}
    @自定義過濾器 與jinja2類似 看下文件。
    ----------------------
    9,資料庫  的orm  物件關係對映
    安裝包pymysql,
    9.1 在專案同名資料夾下的init檔案中,
    import pymysql
    pymysql . install_as_MySQLdb()
    @解釋 就是pymysql升級了  但是django中沒有升級內部介面名稱,執行後就相當於把pymysql 當作MySQLdb用。
    
    9.2  需要在註冊在資料庫配置中
    引數:DATABASES = {複製一下}
    
    10,定義模型類。
    在應用的model下定義模型類
    語法:
    class ***(models.Model):
    各種語法:複製
    預設主鍵
    預設表名
    明確定義的屬性 和隱含的屬性
    (BookInfo, related_name = '自定義屬性名’)
    
    @自關聯一對多 多對多等關係
    @onotoone等
    
    11,資料庫遷移
    兩行命令 1 makemigrations 生成遷移檔案。
                2   migrate 執行遷移檔案
    12, 進入shell驚醒資料庫的增刪改查
    @執行記錄查詢語句的配置  相當於echo語句列印吧
    12.1,語法一:
         objects.create()
    建立完成後需要用瀏覽器執行?
      語法2:
    book = BookInfo()
    book.btitle='abc'
    ..
    ..
    book.save()
    save執行了update然後insert
    @比語法一複雜。 一般用於使用者加密後存入資料庫。
    然後post執行???
    @瀏覽器執行相當於什麼???
    12.2單條件 查詢
    get 單一物件
    all() 查詢全部 一般返回給模板遍歷。要結合條件查詢
       filter 滿足條件
      exclude 滿足的排除
      det過濾單一結果。
    @語法 屬性名稱__比較運算子 = 1等於值 id__exeat=1  | id = 1
    2包含  contents
    3 以什麼開頭 startwith
    4 以什麼結尾endwith
    @@@前面加上I是表示不區分大小寫。
    5,isnull  = false 或者ture
    6,範圍 in  1,3  表示或  不是在        1,3 之內
    7, 不等於3  exclude(id = 3)
    8,對日期做運算:__year = 1980  釋出日期為1980年的書。
    9, 日期比較大小  gt=1980.1.1
    大於1980.1.1的日期。」
    —————————
    
    
    DAY 11.27
    
    昨日總結
    1,複習,建立 模型;
    	1:模型類.object.create(屬性1=值1)
    	2:物件型別=模型類()
    2,查詢:
    	模型類.object.方法(屬性_運算子=值)
    	方法:
    	運算子.
    3, F 物件 物件的屬性:
    	F('屬性名稱') 可以讓屬性和屬性比較,而不是直接屬性值來比較,就是變數與變數比較.  
    	bread__gt=F('bcomment') * 2 
    4,Q物件 ,為了連線多個條件語句
    	fillter(Q(bread__gt=20)&Q(pk__lt=3))
    	@pk 表示primary key  上那麼主鍵都可以用.
    5, 邏輯與 & 可以用,代替.
    6, 邏輯或 | ===>管道連線
    7, 邏輯非 ~ ====>對Q物件取非.
    8, aggregate 聚合函式 後面還有che型別
    	aggregate(sum())
    	返回的shug是字典型別資料
    9,count 
    10, 排序  :order_by 以什麼排序 預設升序.
    	降序前面加 - 減號
    11, 沒有查詢分頁方法 使用的是一個類方法.
    12, 關聯查詢:??????
    	使用物件訪問:物件,和屬性
    	查詢語句,物件_屬性_運算子 = 值
    	
    13, 修改
    	put方法
    14, 刪除方法 deleat邏輯刪除
    15, 查詢集:呼叫all() filter() ,exclude()
    order_by() 返回查詢集
    	倆個特性 :1,惰性執行
    			2,快取,減少與mysql互動,迅速.會根據程式碼實現.
    
    16,admin 後臺管理.
     16.1 建立管理員:
     16.2 註冊模型類:
     16.3 修改中文  類屬性增 verbors_name
    17, 後臺管理 之
    	17.1 增刪改差
    	17.2 增加後臺屬性 定義一個類然後作為註冊的屬性傳入修改效果.
    	17.3 strftime  日期轉字串.
    		 strptime  字串轉換日期格式.
    	17.4 list_display 列表頁面顯示什麼方法
    	17.5 list_filter 列表頁面以什麼過濾
    	17.6 search_fields 列表頁面搜尋選項. 需要指定搜尋範圍.
    18, 編輯頁的屬性修改
    	fields=[屬性名稱]  設定編輯頁面的屬性
    	用於編輯頁面的高階修改  就跟設定某些頁面的高階選項一樣.  不寫的屬性不會顯示.
    	fieldsets=(
    		('組名',{'fields':[屬性名])
    		('組名',{'fields':[屬性名)	
    	)
    
    19, 內嵌類  定義類 加引數 然後在書籍列表嵌入英雄.   可以給書籍列表增加修改專案 
    	列表新式內嵌 和 form表單新式內嵌.
    20, 後臺頁面的標題  
       3個  標籤標題
    		首頁標題
    		主頁標題
    
    21, 上傳圖片.檔案儲存在磁碟  名字或者地址儲存到資料庫.
    	1,完成配置,建立目錄,配置地址.
    	2,模型類中定義屬性,ImageField
    @需要資料庫遷移,需要檔案copy   ???如果不copy的話會怎麼樣?
    @需要在medie下新建儲存庫.
    	3,如果需要管理編輯在admin下的管理模型下面還要註冊新增類.
    	@圖片儲存路徑構造: media/////
        4, 訪問需要註冊檔案目錄.
    	
    
    DAY 11.28
    
    昨日總結
    
    資料庫操作
    	增加:
    		物件=模型類.objects.create(屬性1=值1,...)
    		建立物件,屬性賦值,物件.save()
    	查詢
    		模型類.objects.方法(屬性__運算子=值)
    		方法:all()===>不能寫引數
    			filter(),get,exclude()====>寫查詢條件
    			order_by(),aggregate()
    		型別:F(屬性)
    			Q(條件語句)==》實現邏輯與&、或|、非~
    			說明:邏輯與可以通過逗號連線
    	修改
    		模型類.objects.filter(條件).update(屬性1=值1,...)
    		查詢物件,修改屬性,物件.save()
    	刪除
    		模型類.objects.filter(條件).delete()
    		查詢物件,物件.delete()
    
    @還沒寫到admin  寫到了  但是遷移不了資料庫
    
    @後臺管理admin
    	1.建立管理員
    	2.在應用的admin.py中註冊模型類
    		class ***Admin(admin.ModelAdmin):
    			重寫屬性,實現頁面效果
    		admin.site.register(模型類,***Admin)
    	3.在後臺中可以完成資料的增加、修改、刪除、查詢
    
    @repr 和str 方法顯示字串
    
    @shell(輸入python)輸出物件時會執行這個物件的repr方法
    
    @在終端輸出 比如pycharm裡輸出的那個終端使用str的方法.
    
    @ shell  計算機直譯器介面
    今日筆記
    1,DRF Django REST framwork
    2, web應用模式 :
    	前後段不分離 接受資料 處理資料 返回資料
    	前後段分離  處理資料返回資料  返回json
    區別在與試圖負責的工作,  不分離的試圖負責呼叫模板 生成html返回.  分離的試圖負責構造json返回資料. 
    3, RESTful 設計方法.
        一般寫介面就是儘量在一個類中定義不同方法.遵守規範. 這個規範即使RESTful規範.
        含義:規定編寫介面的規則:    
        1,應用域名儘量體現api
        2, 版本資訊體現.
    
    @案例
    圖書
    	操作:crud
    	定義檢視:get,post,put,delete
    	路由規則:一條
    英雄
    	操作:crud
    	定義檢視:get,post
    	路由規則:三條
    -----------與程式碼有關部分-------------
        3, 路徑:名詞 複數 原則.
        4,HTTP動詞都是請求方法  所以和路徑區別開了.
    	常用四個  模型類的方法
               GET () 查尋 無資料也是get方法
               POST ()  新建
               PUT  () 修改
               DELETE ()  刪除
       	不常用四個 PATCH ()
       			HEAD  ()
       			OPTIONS () 
         5,
         6,狀態碼 
           200 ----ok          
           201 ---- created    建立成功
           204 ---- no content 無返回值 刪除成功
           
           301 ----moved permanently
           
           400 ----bad request
           403 ----forbidden
           404 ----not found
           406 ----methods not allowd
          7,處理異常
          8,返回結果
          增刪改查要返回結果.
          
          9, 超媒體 API的文件資訊 讓前端或者使用者閱讀.
          10, 資料格式 json格式 
    -----------------------------------
    
    3,程式碼實現
    請求方式   處理         路徑            響應      狀態碼
    get ------查詢--------books/----------json-------200
    post------查詢--------books/----------json-------201
    get2----根據主鍵---books/(?P<pk>\d+)/--json-------200
    put-------修改----books/(?P<pk>\d+)---json--------201
    delete----刪除-----books/(?P<pk>\d+)----無---------204    
        
    @實現: 建立工程---建立應用---複製模型---配置資料庫---註冊應用---註冊路由---建立檢視
    
    @格式和轉換失敗 報錯 非json不識別  json認識的是字典 需要將返回的物件轉字典
    
    @實現就是用一個新列表遍歷得到的資料並append進去,需要提取key,並注意轉換日期格式.
    
    @序列化  就是將沒有序列的結果轉換的有序列 序列惡意理解為就是變成字典格式. 
    
    @json是一種資料格式 與語言無關 他的來源是基本額資料格式,所以查詢到的資料必須通過格式化再轉成json.  基本資料格式(int float tuple set
    str 字典)
    如
     {
          "bpub_date": "2018-11-28",
          "btitle": "西遊記456"
        }
    
    4, post方法
    建立(post傳輸)
    接收:   json_dict = json.loads(request.body.decode())
    
    book = BookInfo.objects.create(**json_dict)
    
    return JsonResponse({
            '':''
            '':''
    })
    @注意不要轉換日期格式
    @stats = 201 加在return內 可以轉換狀態碼
    
    5, get 根據主鍵查詢一個物件
    
    
    6,修改 put方法
        1,接收
        2,驗證
        3,查詢   需要判斷 
           儲存 save()
        return jasonresponse
    7,刪除 delete
           注意返回資訊
           
           
    8 , 封裝重複程式碼  
    
    @試一下在哦啊上的返回書包含的英雄資料是否可以轉換成列表 
    
    9,DRF 框架 實現操作資料庫轉換json格式
     序列化 :物件轉換成json
     反序列化 :字典json轉換成物件  裡面包含了驗證的過程.
     @完成了封裝和後臺互動.
    環境搭建
    1.pip install 
    2.在settings.py中INSTALLED_APPS=['rest_framework']
     
    10, 序列化器類 serializer serializer
        10.1 定義 建立serializers檔案 並定義 序列化器型別.
        導包---定義類
        serializer類:
        class ***Serializer(類的父類)
        屬性=型別
        @屬性與模型類的屬性同名 就可以.
     包含資料型別  字串 日期 數字 
        10.2 重點選項說明
            read only  只讀 只用於輸出.  = bollen
            write only 只寫 反序列化的輸入 不用與輸出
            required---->必須,如果請求報文中沒有這個屬性,則拋異常
    11,  程式碼實現序列化器
         序列化多個物件(blist,many=True)!!!
        return (safe=False)
    格式
    serializer=序列化器型別(模型類物件)
    serializer.data====>返回字典
    12 關係屬性的序列化
    引數不同  關係型欄位 並設定rea_only = True
    返回關聯引數如果需要輸出為字串而不是關係欄位,需要字串需要呼叫字串方法,並指定屬性是read_only
    3種輸出方案:
    \	1.輸出主鍵
    	2.輸出字串
    	3.輸出自定義序列化器
    ------資料需求是功能決定的----
     
    
    
     DAY 11.30
    複習
    1,RESTful規範
    2,序列化與反序列化 類似與數字化反數字化等,我們的角度是將字典與物件來回轉換.
    3,呼叫序列化語法:
    	1,serializer = ***Serializer(data=zidian)
    	2,serizlizer.is_valid()
    	驗證是否序列化
    	3,save() 
    ----->程式碼實現:
    
    4,驗證方式
     1,驗證型別 和 選項 
    required=False?  必須=false 表示如果有這個引數,但是沒寫不報錯.
     2,自定義驗證方法,驗證單個屬性
    ----->程式碼塊
    def validate_btitle(self,value):
    	#驗證是否包含django欄位
    	if 'django' not in validate: 
    		raise serializers.ValidationError('書名必須包含django')
    	return value	
    @驗證就是為了找錯,不用try expect
     3,針對多個屬性比較驗證.
    def validate(self,attrs):
    	bread = attrs.get('bread')
    	bcomment = attrs.get('bcomment')
    	if bread < bcomment:
    		raise serializers.ValidationError('閱讀量必須大於等於評論量')
    
    	return attrs
    @返回值不提示具體欄位,因為這不是某一個欄位的錯誤.
     
    4, 建立物件
    	呼叫save() 方法實際執行create()方法.
        所以需要重寫create才嫩實現儲存.
    ----->程式碼塊
      引入模型,然後呼叫類方法:
    def create(self, validated_data):
     book = BookInfo.objects.create(**validated)
     return book
    
    5,修改方法 
    ----->程式碼塊
        def update(self, instance, validated_data):
            # 修改
            # instance===>待修改的物件
            # validated_data===>接收的,經過驗證後的資料
            # 對於必傳屬性,直接獲取並賦值
            instance.btitle = validated_data.get('btitle')
            instance.bpub_date = validated_data.get('bpub_date')
            # 對於可傳屬性,判斷再賦值
            if validated_data.get('bread'):
                instance.bread = validated_data.get('bread')
            if validated_data.get('bcomment'):
                instance.bcomment = validated_data.get('bcomment')
            instance.save()
            return instance
    呼叫程式碼:需要傳入物件名 和新的值.
    
    @建立方法 和修改方法 意義不同,如果沒有建立 就不能修改.但是如果已經建立,修改可以多次執行,並且會實現相當於覆蓋的效果的建立方法.???
    
    6,模型類序列化器:
    ModelSerializer方法 比 serializer進行更高階的封裝.
    封裝的部分:1,定義屬性的程式碼,
    		  2,
    未封裝的部分:驗證方法 等
    ----->程式碼塊.
    class HeroSerializer(serializers.ModelSerializer):
        # 隱藏屬性,需要明確定義
        hbook_id = serializers.IntegerField()
        # 關係屬性
        hbook = serializers.StringRelatedField(read_only=True)
    
    @注意單獨輸出屬性用fields=[]列表指明.
    @比如隱含屬性  可以明確定義 下面不管用類方法還是單獨方法輸出都不會報錯.
    @新增格外屬性的方法:
    ------>程式碼塊.
        # 自定義驗證方法
        def validate_hname(self, value):
            # 要求:姓郭
            if not value.startswith('郭'):
                raise serializers.ValidationError('姓郭')
            return value
    @為啥沒覺得沒怎麼封裝啊??
    
    7,選擇序列化器:
     標準:是否操作模型類
    
    8,DRF的檢視部分:(另一部分是序列化器)
    @新物件方法
    @新的類:重點APIVIEW
    ----->程式碼塊
    #匯入包 :from rest_framework.views import APIView
    #建立類()繼承自apiview
    class HerosView(APIView):
        def get(self, request):
            # 接收查詢字串中的條件
            pk = request.query_params.get('pk')
            ordering = request.query_params.get('ordering')
            # 查詢多個
            hlist = HeroInfo.objects.filter(pk__gt=pk).order_by(ordering)
            serializer = serializers.HeroSerializer(hlist, many=True)
    		#返回response()
    		return Response(serializer.data)
    @查詢一個物件:
    ----->程式碼塊
    class HeroView(APIView):
        def get(self, request, pk):
            # 根據主鍵查詢一個
            try:
                hero = HeroInfo.objects.get(pk=pk, is_delete=False)
            except:
                return Response({'msg': '無效編號'}, status=status.HTTP_404_NOT_FOUND)
            # 序列化
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data)
    
    @post修改
    ----->程式碼塊
        def post(self, request):
            # 建立
            # 1.接收
            json_dict = request.data
            # 2.驗證、儲存
            serializer = serializers.HeroSerializer(data=json_dict)
            # 驗證失敗拋異常
            serializer.is_valid(raise_exception=True)
            hero = serializer.save()  # ===>create()
            # 3.響應
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
    
    @注意沒有定義建立個修改方法,因為繼承了
    
    @delete()
    ----->程式碼塊
        def delete(self, request, pk):
            # 刪除指定主鍵的物件
            # 1.查詢
            try:
                hero = HeroInfo.objects.get(pk=pk)
            except:
                return Response({'msg': '無效編號'}, status=status.HTTP_404_NOT_FOUND)
            # 2.刪除
            # hero.delete()
            hero.is_delete = True
            hero.save()
            # 3.響應
            return Response(status=status.HTTP_204_NO_CONTENT)
    注意要執行save()
    @patch() 方法修改部分屬性,可以不傳必傳引數. 實現部分屬性修改,需要在序列化器裡傳入partial=true  指明部分屬性修改.並用patch方法訪問.
        def patch(self, request, pk):
            # 修改部分屬性
            # 1.查詢:
            try:
                hero = HeroInfo.objects.get(pk=pk)
            except:
                return Response({'msg': 'not found'}, status=status.HTTP_404_NOT_FOUND)
            json_dict = request.data
            # 區域性修改關鍵字 partial=True
            # 還注意傳引數需喲字典,並且注意逗號.
            serializer = serializers.HeroSerializer(hero, data=json_dict, partial=True)
            serializer.is_valid(raise_exception=True)
            hero = serializer.save()
            serializer = serializers.HeroSerializer(hero)
            return Response(serializer.data)
    
    總結:
    1,序列化過程:
    2,序列化器實現資料的建立和更新:
    (建立,save())
    區別在於建立序列化器部分的傳入引數;
    3,用drf的request獲取請求資料
    data 獲取請求體的資料
    query_params  獲取查詢字串
    其他好像一樣.
    3,使用response構造響應物件.
    4,使用序列化器盡心反序列化操作:驗證,與儲存
    4.1,實現驗證方式:
    三個部分驗證:1,2,3,
    4.2 實現儲存 create()
                update()
    5,認識APIView檢視 
    
    (小結:必背部分
    list-------查詢多個 get
    retrieve---查詢一個get
    create-----建立post
    update-----修改put
    destroy----刪除 delete
    )
    
    
    
    DAY 12.1
    1,GenericAPIView  框架
    兩個屬性重點:
    queryset = 查詢集
    serializer_class = 序列化器類
    五個操作型別的函式:
     ListModelMixin
     CreateModleMixin
     RetrieveModelMixin
     
    
    
    ----->程式碼塊
    # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
    class BookDetailView(GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            book = self.get_object()
            serializer = self.get_serializer(book)
            return Response(serializer.data)
    
    
    @先寫檢視的大概模型,同時寫serializer類  ,需要什麼序列化器趕緊加上.
    
    @繼承的關係 MRO順序
    ----->程式碼塊
    class BookDetailView(APIView, GenericAPIView):
    
    @習慣上先寫misin的類 再寫generic的類
    2,查詢全部,的get和post方法ListModelMixin
    ---->程式碼塊
    from rest_framework.mixins import ListModelMixin
    
    class BookListView(ListModelMixin, GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request):
            return self.list(request)
    
    @只能適合統一路由的,路由不同不能繼承.
    3,:查詢一個 修改 更新 檢視:
    ----->程式碼塊:
    class BookDetailView(RetrieveModelMixin, GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            return self.retrieve(request)
    
    @注意patch方法:
    ---->程式碼塊
    @注意刪除方法:物理刪除和邏輯刪除:
    ---->程式碼塊
    class DestroyModelMixin(object):
        """
        Destroy a model instance.
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
    
        def perform_destroy(self, instance):
            instance.delete()
    
    4,進一步簡化:統一路由下的方法抽取:
    ----->程式碼塊
    @方法省略.因為請求方法是固定不能修改的得 ,由瀏覽器決定.
    ----->類名分類:
    5,不存在的類原因:UpdateDsetroyAPIView
    需自定義,其實不存在這個情況.不會先更新一個再刪除一個的情況????
    
    6.單數查詢一個大類:ListAPIView:
    ----->程式碼塊;
    提供 get 方法
    繼承自:GenericAPIView、ListModelMixin
    @因為類都封裝了,所以以後一般工作在驗證.
    
    ??????
    實現
    
    
    7,進一步封裝試圖類: 檢視集ViewSet:
    	.list() 提供一組資料
    retrieve() 提供單個數據
    create() 建立資料
    update() 儲存資料
    destory() 刪除資料
    
     @解決的1問題:路由規則封裝
    ----->as_view({方法的字典})
    urlpatterns = [
        url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
        url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
    ]
    
     @解決問題2:重寫dispatch
    @#@ 提供的引數 action 因為請求方法一致,但是具體的行為不一樣,就指定action引數來分配具體方法.
    
    @類似於男廁女廁兩大類,但是具體inque進去小便還是大便不一樣,並且小便大便還有可以選擇噸位還是站位,!!!
    
    @最最最終類:ModelViewSet!!!
    包括了增刪改查所有類的方法.
    
    @查詢類 ReadOnlyViewSet
    
    @新增自定義方法 用裝飾器@action(methods=['get], detail=False)
    
    引數詳解:
    def get_serializer_class(self):
        if self.action == 'create':
            return OrderCommitSerializer
        else:
            return OrderDataSerializer
    常用檢視集父類
    1) ViewSet
    繼承自APIView,作用也與APIView基本類似,提供了身份認證、許可權校驗、流量管理等。
    在ViewSet中,沒有提供任何動作action方法,需要我們自己實現action方法。
    2)GenericViewSet
    繼承自GenericAPIView,作用也與GenericAPIVIew類似,提供了get_object、get_queryset等方法便於列表檢視與詳情資訊檢視的開發。
    3)ModelViewSet
    繼承自GenericAPIVIew,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
    4)ReadOnlyModelViewSet
    繼承自GenericAPIVIew,同時包括了ListModelMixin、RetrieveModelMixin。
    檢視集中定義附加action動作
    在檢視集中,除了上述預設的方法動作外,還可以新增自定義動作。
    新增自定義動作需要使用rest_framework.decorators.action裝飾器。
    以action裝飾器裝飾的方法名會作為action動作名,與list、retrieve等同。
    action裝飾器可以接收兩個引數:
    methods: 該action支援的請求方式,列表傳遞
    detail: 表示是action中要處理的是否是檢視資源的物件(即是否通過url路徑獲取主鍵)
    True 表示使用通過URL獲取的主鍵對應的資料物件
    False 表示不使用URL獲取主鍵
    舉例:
    from rest_framework import mixins
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.decorators import action
    
    class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        # detail為False 表示不需要處理具體的BookInfo物件
        @action(methods=['get'], detail=False)
        def latest(self, request):
            """
            返回最新的圖書資訊
            """
            book = BookInfo.objects.latest('id')
            serializer = self.get_serializer(book)
            return Response(serializer.data)
    
        # detail為True,表示要處理具體與pk主鍵對應的BookInfo物件
        @action(methods=['put'], detail=True)
        def read(self, request, pk):
            """
            修改圖書的閱讀量資料
            """
            book = self.get_object()
            book.bread = request.data.get('read')
            book.save()
            serializer = self.get_serializer(book)
            return Response(serializer.data
    
    
    具體路由區別
    ---->
    urlpatterns = [
        url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
        url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
        url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
        url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
    ]
    
    8,路由集:
    ----->程式碼塊 
    from rest_framework import routers
    
    router = routers.SimpleRouter()
    router.register(r'books', BookInfoViewSet, base_name='book')
    2)新增路由資料
    可以有兩種方式:
    urlpatterns = [
        ...
    ]
    urlpatterns += router.urls
    
    或
    urlpatterns = [
        ...
        url(r'^', include(router.urls))
    ]
    
    @可以設定為快捷鍵模板
    @DefaultRouter() 和 SimpleRoute()
    區別?
    DefaultRouter與SimpleRouter的區別是,DefaultRouter會多附帶一個預設的API根檢視,返回一個包含所有列表檢視的超連結響應資料。
    9,配置環境:
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',   # 基本認證
            'rest_framework.authentication.SessionAuthentication',  # session認證
        )
    }
    
    在檢視中單獨設定
    
    也可以在每個檢視中通過設定authentication_classess屬性來設定
    from rest_framework.authentication import SessionAuthentication, BasicAuthentication
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        authentication_classes = (SessionAuthentication, BasicAuthentication)
        ...
    
    @認證作用域的要點:
    
    @許可權要求中的不同區別,也有作用域
    
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        permission_classes = (IsAuthenticated,)
        ...
    
    @限流  限制頻率/100值得是次數.
    可以限制頻率單位,可以給小視訊分類點選次數.
    ??這個次數記錄到哪裡了?
    只針對獨立使用者,不累加.
    
    from rest_framework.throttling import UserRateThrottle
    from rest_framework.views import APIView
    
    class ExampleView(APIView):
        throttle_classes = (UserRateThrottle,)
        ...
    
    
    @過濾:
    不支援模糊查詢.
    pip install django-filter
    
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
        filter_fields = ('btitle', 'bread')
    
    # 127.0.0.1:8000/books/?btitle=西遊記
    
    @排序 相當於之前排序的程式碼部分
    
    @分頁 每頁幾條資料 單位是條 不是行 會附加頁碼資訊和連線.
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 100  # 每頁數目
    }
    關閉分頁
    pagination_class = None
    
    from rest_framework.pagination import PageNumberPagination
    
    class StandardPageNumberPagination(PageNumberPagination):
        page_size_query_param = 'page_size'
        max_page_size = 10
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all().order_by('id')
        serializer_class = BookInfoSerializer
        pagination_class = StandardPageNumberPagination
    
    # 127.0.0.1/books/?page=1&page_size=2
    
    ####@###$#這些功能大部分都嫩單獨寫入試圖類,以達到針對性限制.
    
    @異常處理 之後會重寫該功能 以達到log記錄異常.
    
    	APIException 所有異常的父類
    ParseError 解析錯誤
    AuthenticationFailed 認證失敗
    NotAuthenticated 尚未認證
    PermissionDenied 許可權決絕
    NotFound 未找到
    MethodNotAllowed 請求方式不支援
    NotAcceptable 要獲取的資料格式不支援
    Throttled 超過限流次數
    ValidationError 校驗失敗
    
    
    @版本資訊
    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
    }
    
    
    @生成專案文件!!!!
    pip install coreapi
    
    設定介面文件訪問路徑
    在總路由中新增介面文件路徑。
    文件路由對應的檢視配置為rest_framework.documentation.include_docs_urls,
    引數title為介面文件網站的標題。
    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        ...
        url(r'^docs/', include_docs_urls(title='My API title'))
    ]
    
    
    導包----配置路由規則-----訪問  
    (相當於網頁形式的專案說明) 
    包含多個方法的檢視,在類檢視的文件字串中,分開方法定義,如
    class BookListCreateView(generics.ListCreateAPIView):
        """
        get:
        返回所有圖書資訊.
    
        post:
        新建圖書.
        """
    就是註釋 並讓網頁看得到的註釋
    
    
    文件網頁
    瀏覽器訪問 127.0.0.1:8000/docs/,即可看到自動生成的介面文件。
    
    需要中文的化要在試圖裡用文件註釋.
    序列化器里加引數help_text 和admin用的文件說明不衝突.
    
    10 美多商稱專案
    
    @看得到的不是新的,想得到才是
    
    模組分析 5大模組:
    1使用者
    2 商品
    3 購物中心
    4 訂單
    5 支付
    
    @自身價值
    1溝通能力
    2程式碼基本能力
    3解決問題能力  (如果解決不了,記住也是方法) 
    
    DAY 12.3
    
    總結:及時複習
    2,複習drf附加功能
    GenericAPIView+5個ModelMixin:
    	完成crud的操作
    	queryset=查詢集
    	serializer_class=序列化器型別
    ModelViewSet:
    	將多個類中的方法寫在同一個檢視為
    	屬性action:表示當前請求需要呼叫的方法名稱
    	裝飾器action:可以在5個方法的基礎上,增加新的方法
    		methods=[請求方式]
    		detail=True或False:是否接收主鍵
    DefaultRouter:
    	在使用檢視集時,通過這個類的物件生成路由規則
    	router=DefaultRouter()
    	router.register(字首,views.檢視集型別,base_name=字首)
    	urlpatterns+=router.urls
    drf配置(複製):
    	身份認證的方式
    	許可權判斷
    	流量控制
    	過濾
    	排序
    	分頁
    	讀取版本
    	異常處理
    	生成文件
    
    1,建立工程目錄:
    2,修改導包路徑 
    	import sys 裡面是直譯器查詢包的路徑列表  sys.path.insert(0, os.psth.join(BASE_DIR,'apps')
    @ 不設定也可以需要每次拼接路徑
    3,新建資料庫:
    	@新建使用者---create user meiduo_sy18 identified by 'meiduo';
                 分配許可權---grant all on meiduo18.* to 'meiduo_sy18'@'%';(分配給使用者 操作資料庫在任意ip.
    4,連線資料庫,初始化
      配置快取並連線redis 配置資訊 注意安裝django-redis
    5,配置本地化資訊
    6,註釋csrf中介軟體,模板檔案目錄.
    7,註冊框架 rest_fraework
    8,註冊日誌模組.logging
    @注意修改路徑  向上轉移層
    9,異常處理:新建utils工具包,新建exceptions異常檔案.  複製
       #@#@:配置drf的異常處理器
    
    10::::檢視老師程式碼從gitee上看.
    
    11, 設定域名
    	在host檔案下修改域名對映
    	11.1 訪問前端路由對映
    	11.2 訪問後端檔案路由對映.
    	2.www.meiduo.site=========>訪問前端
    	3.api.meiduo.site==========>訪問後端
    	 @那以後的路由呢?
    12,跨域問題解決
       12.1 瀏覽器訪問前端檔案域名 返回的html解釋 css 渲染 js 執行
    	12.2 axios.post請求,js的執行會訪問後端檢視,就發生了跨域 需要發起options試探.
    	@解決方式匯入django-cors-headers包,註冊應用----註冊corsheaders中介軟體-----配置訪問白名單(也就是從哪個域名下的請求), 以達到每個檢視後面自動新增optipns 200狀態.(此狀態就是cookies)
    	@此問題的根源是前後端分離 或者說是前後端域名不同
    	@如果不解決這個問題 會怎樣?
    
    13,具體業務邏輯
    	13.1 使用者模組 ---models
    	auth使用者認證模組 修改
    	@1,導包----定義(繼承			AbstractUser)---自定義欄位(如手機號)---覆蓋原類----遷移
    	@2,定義欄位型別判斷原則: 怎麼讀出來的! 按個十百千讀	出來的是數字,按字元讀出來的是	字串.
    	@3,配置 設定可以覆蓋模型類設定
    	@4,遷移 
    
    
    -----------------
    14,註冊功能--註冊頁面 
     @圖新驗證碼是為了防止重複向手機發送驗證碼.
    	14.1 發簡訊 過程
    	@1,先判斷髮送標記.
    	@2,生成---存入傳送標記---存入redis---傳送簡訊
     新建應用
    @沒有包提示 設定檔案為source root
    @沒有模型類操作 繼承自APIVIew 用get方法 
    @連線redis資料庫()
    @讀取標記 命名 拼接字串.
    @生成速記簡訊驗證碼
    @驗證碼存入shuck會覆蓋,
    @呼叫第三方平臺發簡訊.
    @非同步發簡訊
    @傳送簡訊不應該相信客戶端 需要驗證資料庫.
    @pipeline 管道 相當於查詢集類似 就是快取命令,最終執行,減少與資料庫互動次數. ??????/不太懂
    ---->程式碼實現
        url(r'^sms_code/(?P<mobile>1[3-9]\d{9})/$',views.SMSCodeView.as_view()),
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django_redis import get_redis_connection
    from utils.ytx_sdk.sendSMS import CCP
    from rest_framework import exceptions
    import random
    from . import contants
    
    
    class SmsCodeView(APIView):
        def get(self, request, mobile):
            '''
            傳送簡訊驗證碼
            :param request: 請求物件,不需要傳遞
            :param mobile: 手機號,需要傳遞
            :return: 是否傳送成功
            '''
            # 資料儲存在redis中,所以獲取redis連線
            redis_cli = get_redis_connection('verify_codes')
    
            # 判斷此手機號60秒內是否發過簡訊,如果已經發送則不傳送
            sms_flag = redis_cli.get('sms_flag_' + mobile)
            if sms_flag:
                raise exceptions.ValidationError('傳送驗證碼太頻繁')
    
            # 隨機生成6位簡訊驗證碼
            sms_code = random.randint(100000, 999999)
    
            #使用管道方式互動redis
            redis_pipeline=redis_cli.pipeline()
            # 儲存驗證碼
            redis_pipeline.setex('sms_code_' + mobile, contants.SMS_CODE_EXPIRES, sms_code)
    
            # 儲存60秒傳送標記
            redis_pipeline.setex('sms_flag_' + mobile, contants.SMS_FLAG_EXPIRES, 1)
    
            #執行
            redis_pipeline.execute()
    
            # 傳送簡訊
            # sms_code_expires = contants.SMS_CODE_EXPIRES / 60
            # CCP.sendTemplateSMS(mobile,sms_code,sms_code_expires,1)
            print(sms_code)
    
            # 響應
            return Response({'message': 'OK'})
    
    15, celery 非同步
      15.1作用使用框架時,檢視函式實現非同步.
      15.2 構成:@1:task:耗時任務,且耗時任務的結果和響應無關的東西  可以放在celery中做非同步操作.
      		@2:worker:新今晨,執行任務程式碼.
    		@3:broker:呼叫任務時,新增任務到佇列,並通知worker執行.通常為redis等nosql資料庫,記錄資訊的.
    		@4:queue: 儲存執行的任務
    		@5:任務函式.delay(引數)
    ----->程式碼實現
    pip install celery
    
    @建立celery資料夾//
    在celery_tasks包下建立config.py檔案,用於儲存celery的配置資訊
    broker_url='redis://127.0.0.1:6379/14'
    
    @建立任務,註冊任務
    在celery_tasks包下建立main.py檔案,用於作為celery的啟動檔案
    from celery import Celery
    
    # 為celery使用django配置檔案進行設定
    import os
    if not os.getenv('DJANGO_SETTINGS_MODULE'):
        os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_api.settings'
    
    # 建立celery應用
    app = Celery('meiduo')
    
    # 匯入celery配置
    app.config_from_object('celery_tasks.config')
    
    # 自動註冊celery任務
    app.autodiscover_tasks(['celery_tasks.sms'])
    
    @啟動工人???????? 在所有非同步任務都註冊好後啟動,只能手動啟動???/:      
    
    在celery_tasks/sms/包下建立tasks.py檔案,用於儲存傳送簡訊的非同步任務
    import logging
    
    from celery_tasks.main import app
    from utils.ytx_sdk.sendSMS import CCP
    
    logger = logging.getLogger("django")
    
    
    @app.task(name='send_sms_code')
    def send_sms_code(mobile, code, expires, template_id):
        """
        傳送簡訊驗證碼
        :param mobile: 手機號
        :param code: 驗證碼
        :param expires: 有效期
        :return: None
        """
    
        try:
            # result = CCP.send_template_sms(mobile, [code, expires], template_id)
            result = 0
            print(code)
        except Exception as e:
            logger.error("傳送驗證碼簡訊[異常][ mobile: %s, message: %s ]" % (mobile, e))
        else:
            if result == 0:
                logger.info("傳送驗證碼簡訊[正常][ mobile: %s ]" % mobile)
            else:
                logger.warning("傳送驗證碼簡訊[失敗][ mobile: %s ]" % mobile)
    
    @呼叫任務.任務函式.delay(引數)
    在verifications/views.py中改寫SMSCodeView檢視,使用celery非同步任務傳送簡訊
    from celery_tasks.sms.tasks import send_sms_code
    
    class SMSCodeView(GenericAPIView):
        ...
            # 傳送簡訊驗證碼
            sms_code_expires = str(constants.SMS_CODE_REDIS_EXPIRES // 60)
            send_sms_code.delay(mobile,sms_code,sms_code_expires,1)
    
            return Response({"message": "OK"})
    啟動工人命令
    celery -A celery_tasks.main worker -l info
    
    
                                                      
    
    作業:讀文件
    	https://yiyibooks.cn/xx/Django_1.11.6/topics/auth/index.html
    	https://jwt.io/
    	http://getblimp.github.io/django-rest-framework-jwt/
    
    
    DAY 12.4
    昨日總結:
    	1,跨域問題解決方案:每個檢視中都響應options. 實現過程就是請求鉤子類似在中介軟體中加入響應.
    	2,celery實現非同步,主要是為了提升使用者體驗.在一個程序中實現不阻塞程序. 非同步是實現的後端和前端的互動,更客戶端沒多大關係.
    	3,配置混亂的解決方法,提綱
    
    今日
    1 判斷使用者名稱是否存在:
    	1.1 判斷繼承自哪個類  判斷依據是繼承來能不能直接用 返回個數.
    	1.2 只要判斷使用者名稱返回個數是大於0的 就說明存在.
    	1.3 返回結果 按文件來.
    	1.4 前端校驗 就是判斷下是否失去焦點後還會不會有異常提示.
    	@連線資料庫失敗的異常已經被預設處理,不用判斷提示.
    	@filter 方法不會丟擲異常,查不到就是0.
    2,判斷手機號是否存在   和上面一樣.
    ------>程式碼塊
    # url(r'^usernames/(?P<username>\w{5,20})/count/$', views.UsernameCountView.as_view()), 
    class UsernameCountView(APIView):
        """
        使用者名稱數量
        """
        def get(self, request, username):
            """
            獲取指定使用者名稱數量
            """
            count = User.objects.filter(username=username).count()
    
            data = {
                'username': username,
                'count': count
            }
    
            return Response(data)
    
    3,註冊
    	3.1 繼承自g.create
    	3.2 建立不需要查詢集 需要序列化,指定序列化器型別
    ---->序列化器程式碼塊
    from rest_framework import serializers
    import re
    from django_redis import get_redis_connection
    from .models import User
    
    
    class CreateUserSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(
            min_length=5,
            max_length=20,
            error_messages={
                'min_length':'使用者名稱為5-20個字元',
                'max_length':'使用者名稱為5-20個字元'
            }
        )
        password = serializers.CharField(
            write_only=True,
            min_length=8,
            max_length=20,
            error_messages={
                'min_length':'密碼為8-20個字元',
                'max_length':'密碼為8-20個字元'
            }
        )
        mobile = serializers.CharField()
        password2 = serializers.CharField(write_only=True)
        sms_code = serializers.CharField(write_only=True)
        allow = serializers.CharField(write_only=True)
    
        def validate_username(self,value):
            if User.objects.filter(username=value).count()>0:
                raise serializers.ValidationError('使用者名稱存在')
            return value
    
        def validate_mobile(self,value):
            if not re.match('^1[3-9]\d{9}$',value):
                raise serializers.ValidationError('手機號格式不正確')
            return value
    
        def validate_allow(self,value):
            if not value:
                raise serializers.ValidationError('必須同意協議')
            return value
    
        def validate(self,attrs):
            #簡訊驗證碼
            redis_cli=get_redis_connection('verify_codes')
            key='sms_code_'+attrs.get('mobile')
            sms_code_redis=redis_cli.get(key)
            if not sms_code_redis:
                raise serializers.ValidationError('驗證碼已經過期')
            redis_cli.delete(key)
            sms_code_redis=sms_code_redis.decode()
            sms_code_request=attrs.get('sms_code')
            if sms_code_redis!=sms_code_request:
                raise serializers.ValidationError('驗證碼錯誤')
    
            #兩個密碼
            pwd1=attrs.get('password')
            pwd2=attrs.get('password2')
            if pwd1!=pwd2:
                raise serializers.ValidationError('兩次輸入的密碼不一致')
    
            return attrs
    
        def create(self,validated_data):
            user=User()
            user.username=validated_data.get('username')
            user.mobile=validated_data.get('mobile')
            # user.password=validated_data.get('password')
            user.set_password(validated_data.get('password'))
            user.save()
    
            return user
    
    	@考慮接受欄位 現有不包含的自定義宣告
    	@ allow?需要儲存嘛//?是 因為前後端分離,不需要通過前端訪問伺服器.
    	@經判斷直接繼承自Serializer
    ----->程式碼塊.
    	3.3 驗證方法  
    		3.3.1驗證名字是否存在\\\
    		@還是驗證count值 大於0 就是存在.
    		3.3.2 驗證手機號
    		@根據正則表示式判斷格式
    		@根據filter後的count值是否存在
    		3.3.3 判斷密碼是否確認
    		@validate()
    		3.3.4 判斷簡訊驗證碼
    		@驗證簡訊驗證碼需要獲取手機號,所以一個方法獲取不了,需要用validate(self, attrs)方法.
    		@@@@簡訊驗證碼的key裡不是有手機號碼 可以用key獲取嘛?      答,不能,因為只能根據key獲取.
    		@#注意 對比值的時候需要型別相同.
    		@@@@注意除了字串型別 其他的型別都能相互轉換直接.
    		@不需要輸出的 不需要輸入的  加上only引數.
    ---------
    一定不要相信眼睛看到的
    ---------
    4, JWT JSON WEB TOKEN
    	4.1 構成:header.pyload.signature.
    		@頭部資訊 固定
    		@載荷    使用者資訊
    		@根據前兩部分資訊和金鑰算出簽名
    		>>>簡而言之就是 伺服器有個密碼 結合使用者資訊算出一個密碼, 給瀏覽器返回攜帶,來回需要對比簽名,對的就是保持,不對的就不算保持.jwt主要功能不是密保,是多客戶端登入.
    	4.2 配置
    	4.3 註冊成功 狀態保持
    ------>程式碼塊
      @timedelta 時間差
    DAY 12.06
    昨日總結:  
    1,JWT  生成token
    2,判斷會不會,就是能不能實踐.
    3,分支 私有分支--muzhe
    			開發階段合併分支--dev 包括前端等的合併 測試階段
    			master---上線分支 運維部署分支
    4,原則是少寫程式碼 繼承的檢視類 可以手寫最早的APIView的程式碼 然後再封裝.
    內容四段:接受資料---檢查資料----處理資料----響應資料
    
    5,cerely只針對非同步			
    
    ------------------
    
    今日:
    
    1,登入: 用自帶的類 新增路由並指定響應結果.
    				#指定載荷資料 ---->程式碼
    2, 具體檢視原始碼中:obtain_jwt_token 接受post請求,完成登入
       jwt提供檢視,接受post請求,獲得使用者和密碼, 呼叫django中的auth類下面的authenticate方法 完成登入,這個方法讀取配置,沒有配置找到認證類ModelBackend,這個類完成對比使用者和密碼 進行驗證.
    
    3,實現多帳號登入;
    	3.1自定義認證類 並配置 讀取配置類
    	3.2
    ---->程式碼塊
    		#判斷是否是手機號
    		#else 是使用者名稱
    		問題@使用者名稱 如果註冊的是手機號格式,會引起使用者登入錯誤.  解決方法,不讓這麼註冊.
    --------------
    解決錯誤的方法,從源頭改起.
    --------------
    
    4,qq登入
    	4.1 成為開發者
    	4.2 放置圖示
    	-----------以下是後端工作
    	4.3 使用Authorization_Code獲取Access_Token
    	///oauth2.0==協議版本 就是讓開發者使用第三方介面時候能看懂怎麼授權(怎麼看懂對方提供的資料庫資訊)的協議.就是說 你這麼寫才能訪問我們的資料庫.
    	4.4 獲取openid 就是使用者在qq網站的身份資訊的唯一標識.
    	4.5 openid與我們的賬戶資訊繫結.
    	@簡而言之,開發者獲取授權資訊1,並用授權資訊做成連結,客戶點選連結,跳轉到qq的認證伺服器並填寫個人資訊2,qq伺服器判定成功後用資訊1+資訊2生成資訊3==openid,並攜帶資訊3轉回開發者網站,開發者網站得到openid 賦值給對應的註冊資訊欄位,完成註冊.  因為qq的資訊是qq帳號和qq暱稱還有密碼,不包括手機號,所以開發者的網站會要求繫結手機號. 其實頁可以做成就算綁定了包括手機號等資訊,頁可以要求客戶繫結手機號,以達到驗證目的. 所以手機號這個部分會實現了重新存入資料庫的效果.
    	@開發者需要編寫的部分除了前端效果,還有就是將qq授權資訊1編寫成url資訊(拼接字串),還有回撥我的網址.和返回的資訊解讀,賦值成我們需要到的註冊格式,並賦值.
    	
    	4.6.繫結:
    	根據openid判斷是否存在相同內容 就狀態保持 登入.-----沒相同內容,讓前端返回個網站,讓他繫結資訊.----根據繫結的資訊查詢使用者物件,如果有說明使用者登入過,返回資訊讓他檢查或者登入-----如果不存在,建立新使用者,並建立已被qq授權的使用者欄位.
    	
    DAY 12.07
    
    1, baseModel 類是新建時間和更新時間 是優厚很多授權服務都需要用的標誌,所以可以建立一個模型在工具裡,然後讓其他授權模型類繼承.
    2,auto_now_add = True 自動新增建立時候的當前時間.
    	auto_now 頁是當前時間,但是修改時候會修改.
    3, abstarct = True 抽象 = True 不是具體的,不用遷移.
    @類裡面只有兩個欄位 一個是qq唯一標識,和qq賬戶與本地賬戶的依賴關係,  但是這裡嫩儲存其他qq賬戶的資訊嘛?  是!!
    
    4,urllib ----URL的lib協議  一種格式 資源共享協議
    	urlopen(網址) 發起get強求方式的http請求
    	urlencode(字典) 返回查詢字串
      parse_qs(查詢字串)  返回字典
    5, itsdanderous 
      將字典進行加密解密的
      json需要的類
    	serializer=TimedJSONWebSignatureSerializer(私鑰,過期時間)
    提供了 dumos(字典) 返回加密字串
    lodas(加密字串)  返回解密後的字典.
    
    6,獲取url 
    匯入工具包---按照官方的說定義方法---按照方法編寫檢視函式---新增路由資訊---根級路由資訊包含
    QQ網站攜帶授權資訊訪問前端做成的callback網站,這個網站接受資訊然後去訪問後端的伺服器,後端解析攜帶的資訊,存入資料庫等,返回給前端,
    7,解析code 獲取token
    	7.1query_params.get()
    	7.2django裡發起http請求,獲取token
    	@獲取的token過程 轉化字典等???
    8,獲取openid
    	@擷取data.[10:-4]  這裡注意下.  (以後應該嫩用愛爬蟲)
    	@response().read()  讀取響應報文體的部分.
    9, 繫結資訊
    	9.1 使用openid查詢資料
    	9.2 查詢不到提示繫結 ,並加密返回給callback,(跳轉繫結頁面)	
    	9.3 查到了 繫結 
    	繼承檢視類,指定序列化器,驗證access_token資訊.這裡要解密.
    ---------->程式碼
    	@邏輯  獲得的openid沒有標識,在沒有繫結之前沒有任何可以標識的資訊,不能儲存在伺服器,所以需要返回給客戶端(瀏覽器)然後等待他繫結標識資訊,最後一起儲存在資料庫.  這也很好的解決了有的使用者沒有繫結意外中斷,這部分資訊就沒用了,村起來頁沒用.  也可以存進去 然後指定時間內沒用的化刪除?
    	各種驗證:
    --------->程式碼塊
    
    @這段腦袋不轉了 有點糊塗.
    
    10,使用者中心,
    	10.1 檢視類 繼承自retrieveAPIVIEW
    	@queryset決定怎麼獲得資料
    	@serializer決定??
    	@難點 呼叫重寫get_object(self)??
    	10.2 獲取當前登入使用者
    		return self.request.user
    	@主要要求登入 這部分資訊是restframerwork的附帶資訊裡的要求登入許可權部分???
    	10.3 指定序列化器 
    	完成    
    
    ====================總結
    根據QQ登入的開發文件使用SDK實現QQ登入的連結的獲取
    根據QQ登入的開發文件使用SDK實現access_token的獲取
    根據QQ登入的開發文件使用SDK實現openid的獲取
    	0.配置:開發者資訊
    	1.建立物件OAuthQQ
    	2.呼叫方法:
    		get_qq_login_url()====>生成授權頁面的url
    		get_access_token()====>根據code獲取token
    		get_openid()========>根據token獲取openid
    知道使用itsdangerous進行資料簽名的方法及流程
    	tjwss=TimedJSONWebSignatureSerializer(私鑰,過期時間)
    	tjwss.dumps(字典)======>加密字串
    	tjwss.loads(加密字串)===>字典
    		說明:如果過期,或資訊被修改,則拋異常
    理解獲取使用者基本資訊的業務邏輯
    	1.獲取物件
    	2.建立序列化器物件
    	3.輸出
    	關鍵:第1步獲取物件時,需要重寫get_object()方法
    
    
    
    DAY 12.09
    昨天總結: 
    1,get_object()方法.
    2,及時總結當天的知識點.
    3,
    
    今天筆記:
    1,郵箱繫結
    	1.1 請求方式 put 修改內容系列.,指定繼承類,然後要求登入,還要指定查詢集,並指定序列化器.
    	1.2 經過分析,不需要查詢集,當前登入使用者就是當前物件,不用查詢.所以 改寫get_object方法就心.
    	1.3 定義序列化器.
    	在序列化器裡驗證郵箱,就是要傳送郵箱給feu新增的郵箱,
    	1.4 驗證郵箱, 配置郵箱伺服器資訊,使用django自帶的模組send_mail來發送郵箱,
    	方法的引數(主題,訊息內容,///)
    	1.5 耗時操作,建立成非同步任務,
    	任務設定:1建立包,建立任務檔案,定義任務fenian,在主main檔案裡註冊任務.
    	1.6 裝飾傳送方法
    	1.7 使用者取郵箱驗證,點選地址直接請求後臺,後臺伺服器接受資訊,解密獲取使用者id,根據修改模型類郵箱的屬性.
    2, 三級聯動設定收穫地址.
    	2.1 新建areas應用,註冊應用.
    	2.2 省市區查詢,查詢省  listapiview  -- get.
    			根據省id查詢單個市區  用retrveapivie
    			分析後知道用查詢集,就是readonlymodelapiview
    	2.3 查詢省級地區,用get_queryset方法只能查詢一種.
    	2.4 查詢省級下面的地區,雖然只查詢省級的地區,必須查詢所有,才能顯示全部地區,但是由於前面已經限制,所以只顯示市級地區.
    	2.5 顯示市區一級的資訊,必須單獨設定查詢序列化器.
    	@可以看出指定序列化器時候不能滿足情況,就要用get序列化器的方法.
    	@複習retrevr方法的查詢結果.他是根據id查詢一個.
    3, 伺服器優化,快取!
    	3.1 意義,減少資料庫互動,將查詢結果快取起來
[email protected]
快取到哪兒?答 快取到快取裡了,快