The Interview Outline

************************* 一.基礎部分*************************

1.1 經常使用數據類型

- 字符串  split/strip/replace/find/index ...
- 列表     append/extend/insert/push/pop/reverse/sort ...
- 元組     len/max/min/count/index ...
- 字典     keys/values/pop/clear/del ...
- 集合  add/remove/clear/交集&、並集 |、差集 - 
 
- collections  Python內建的一個集合模塊,提供了許多有用的集合類。
    1.Counter是一個簡單的計數器,例如,統計字符出現的個數;
    2.OrderedDict能夠實現一個FIFO(先進先出)的dict,當容量超出限制時,先刪除最先添加的Key;
    3.deque是爲了高效實現插入和刪除操做的雙向列表,適合用於隊列和棧;
    4.defaultdict使用dict時,若是引用的Key不存在,就會拋出KeyError。若是但願key不存在時,返回一個默認值,就能夠用defaultdict;

1.2 位和字節的關係

1.位(bit) 
    來自英文bit,表示二進制位。位是計算機內部數據儲存的最小單位,11010100是一個8位二進制數。一個二進制位只能夠表示0和1兩種狀態;兩個二進制位能夠表示00、0一、十、11四種狀態;三位二進制數可表示八種狀態。  

    2.字節(byte)  
    字節來自英文Byte,習慣上用大寫的「B」表示。  
    字節是計算機中數據處理的基本單位。計算機中以字節爲單位存儲和解釋信息,規定一個字節由八個二進制位構成,即1個字節等於8個比特(1Byte=8bit)。八位二進制數最小爲00000000,最大爲11111111;一般1個字節能夠存入一個ASCII碼,2個字節能夠存放一個漢字國標碼。

1.3 進制轉換

1) 二進制數、轉換爲十進制數的規律是:把二進制數按位權形式展開多項式和的形式,求其最後的和,就是其對應的十進制數——簡稱「按權求和」。
    2) 十進制整數轉換爲二進制整數採用"除2取餘,逆序排列"法。具體作法是:用2去除十進制整數,能夠獲得一個商和餘數;再用2去除商,又會獲得一個商和餘數,如此進行,直到商爲零時爲止,而後把先獲得的餘數做爲二進制數的低位有效位,後獲得的餘數做爲二進制數的高位有效位,依次排列起來。

    10進制,固然是便於咱們人類來使用,咱們從小的習慣就是使用十進制,這個毋庸置疑。
    2進制,是供計算機使用的,1,0表明開和關,有和無,機器只認識2進制。
    16進制,內存地址空間是用16進制的數據表示, 如0x8039326。

1.4 解釋型和編譯型

編譯型:運行前先由編譯器將高級語言代碼編譯爲對應機器的cpu彙編指令集,再由彙編器彙編爲目標機器碼,生成可執行文件,然最後運行生成的可執行文件。最典型的表明語言爲C/C++,通常生成的可執行文件及.exe文件。 

    解釋型:在運行時由翻譯器將高級語言代碼翻譯成易於執行的中間代碼,並由解釋器(例如瀏覽器、虛擬機)逐一將該中間代碼解釋成機器碼並執行(可看作是將編譯、運行合二爲一了)。最典型的表明語言爲JavaScript、Python、Ruby和Perl等。

1.5 垃圾回收機制

Python中的垃圾回收是以引用計數爲主,分代收集爲輔。引用計數的缺陷是循環引用的問題。
    在Python中,若是一個對象的引用數爲0,Python虛擬機就會回收這個對象的內存。

    http://www.cnblogs.com/Xjng/p/5128269.html

1.6 求結果

def num():
        return [lambda x:i*x for i in range(4)]

    print([m(2) for m in num()])
    ---------
    [6, 6, 6, 6]

1.7 read()、readline()和readlines()區別

1) read([size])方法從文件當前位置起讀取size個字節,若無參數size,則表示讀取至文件結束爲止,它範圍爲字符串對象
    2) 從字面意思能夠看出,該方法每次讀出一行內容,因此,讀取時佔用內存小,比較適合大文件,該方法返回一個字符串對象。
    3) readlines()方法讀取整個文件全部行,保存在一個列表(list)變量中,每行做爲一個元素,但讀取大文件會比較佔內存。

1.8 列舉經常使用的內置函數

long(x)
    float(x)  # 把x轉換成浮點數
    complex(x) # 轉換成複數
    str(x)   # 轉換成字符串
    list(x)  # 轉換成列表
    tuple(x) # 轉換成元組
     
    進制相互轉換
     r= bin(10) #二進制
     r= int(10) #十進制

     r = oct(10) #八進制
     r = hex(10) #十六進制
     i= int("11",base=10)#進制間的相互轉換base後跟 2/8/10/16
     print(i)
     
    chr(x)//返回x對應的字符,如chr(65)返回‘A'
    ord(x)//返回字符對應的ASC碼數字編號,如ord('A')返回65
    abs(),all(),any(),bin(),bool(),bytes(),chr(),dict()dir(),divmod(),enumerate(),eval(),filter(),float(),gloabls(),help(),hex(),id(),input(),int(),isinstance(),len(),list(),locals(),map(),max(),min(),oct(),open(),ord(),pow(),print(),range(),round(),set(),type(),sorted(),str(),sum(),tuple()

1.9 filter、map、reduce的做用

filter:對於序列中的元素進行篩選,最終獲取符合條件的序列
    map:遍歷序列,對序列中每一個元素進行操做,最終獲取新的序列
    reduce:對於序列內全部元素進行累計操做

1.10 其餘彙總

1) yield form
        https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&fps=1

************************* 二.函數*************************

2.1 參數是傳值,仍是傳引用?

例一:
"""
def func(a1,a2=[]):
    a2.append(a1)
    print(a2)


func(1) # [1,4]     [1,]
func(3,[]) # [3,]   [3,]
func(4) # [1,4]     [1,4]
"""
例二:
"""
def func(a1,a2=[]):
    a2.append(a1)
    return a2


l1 = func(1)
print(l1)       # [1,]
l2 = func(3,[])
print(l2)       # [3, ]
l3 = func(4)
print(l3)       # [1,4]
"""
例三:
"""
def func(a1,a2=[]):
    a2.append(a1)
    return a2

l1 = func(1)     # l1=[1,4]
l2 = func(3,[])  # l2=[3,]
l3 = func(4)     # l3=[1,4]
print(l2)
print(l1)
print(l3)
"""

2.2 生成器/迭代器/可迭代對象

生成器:一個函數調用時返回一個迭代器,或函數中包含yield語法,那這個函數就會變成生成器;
應用:
-redis獲取值
  conn = Redis(...)
    cursor = '0'
        while cursor != 0:
        # 去redis中獲取數據:12
        # cursor,下一次取的位置
        # data:本地獲取的12條數數據
        cursor, data = self.hscan(name, cursor=cursor,
        match=match, count=count)
        for item in data.items():
          yield item
可迭代對象:一個類內部實現__iter__方法且返回一個迭代器。
迭代器:含有__iter__和__next__方法 (包含__next__方法的可迭代對象就是迭代器)

2.3 偏函數

當函數的參數個數太多,須要簡化時,使用functools.partial能夠建立一個新的函數,這個新函數能夠固定住原函數的部分參數,從而在調用時更簡單。
def add(a,b):

      return a+b;

add(3,5)

add(4,7)

以上兩個是咱們正常調用,那麼若是咱們知道一個已知的參數a= 100,咱們如何利用偏函數呢?

import functools import partial as pto
puls = pto(add,100)
result = puls(9)

result的結果就是109。

在這裏偏函數表達的意思就是,在函數add的調用時,咱們已經知道了其中的一個參數,咱們能夠經過這個參數,從新綁定一個函數,就是pto(add,100),而後去調用便可。


對於有不少可調用對象,而且許多調用都反覆使用相同參數的狀況,使用偏函數比較合適。

************************* 三.模塊*************************

3.1 經常使用到的模塊有哪些?

- re/json/logging/os/sys/requests/beautifulsoup4

3.2 正則表達式

1.匹配手機號示例
import re

def phone(arg):
    s=re.match("^(13|14|15|18)[0-9]{9}$",arg)
    if s:
        return "正確"
    return "錯誤"

print(phone("13722751552"))

二、match和search的區別

re.match只匹配字符串的開始,若是字符串開始不符合正則表達式,則匹配失敗,函數返回None;
re.search匹配整個字符串,直到找到一個匹配。

3.經常使用正則匹配
中  文:[\u4e00-\u9fa5]
手機號:0?(13|14|15|17|18|19)[0-9]{9}
郵  箱:\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}
身份證:\d{17}[\d|x]|\d{15}

4.貪婪和非貪婪
正則表達式一般用於在文本中查找匹配的字符串。Python裏數量詞默認是貪婪的(在少數語言裏也多是默認非貪婪),老是嘗試匹配儘量多的字符;非貪婪則相反,老是嘗試匹配儘量少的字符。在"*","?","+","{m,n}"後面加上?,使貪婪變成非貪婪。
http://tool.chinaz.com/regex/

3.3 根據路徑找文件

import os

path = "C:\Pycharm"
print(os.listdir(path))
# 方法一:(不使用os.walk)
def print_directory_contents(sPath):
    import os

    for sChild in os.listdir(sPath):
        sChildPath = os.path.join(sPath, sChild)
        if os.path.isdir(sChildPath):
            print_directory_contents(sChildPath)
        else:
            print(sChildPath)
            
# 方法二:(使用os.walk)
def print_directory_contents(sPath):
    import os
    for root, _, filenames in os.walk(sPath):
        for filename in filenames:
            print(os.path.abspath(os.path.join(root, filename)))


print_directory_contents('已知路徑')

sPath-- 是你所要便利的目錄的地址, 返回的是一個三元組(root,dirs,files)。
    root 所指的是當前正在遍歷的這個文件夾的自己的地址
    _ 是一個 list ,內容是該文件夾中全部的目錄的名字(不包括子目錄)
    filenames 一樣是 list , 內容是該文件夾中全部的文件(不包括子目錄)

3.4 建立刪除文件

建立目錄
os.mkdir("file") 
 
複製文件
shutil.copyfile("oldfile","newfile") oldfile和newfile都只能是文件
shutil.copy("oldfile","newfile") oldfile只能是文件夾,newfile能夠是文件,也能夠是目標目錄
 
複製文件夾
shutil.copytree("olddir","newdir") olddir和newdir都只能是目錄,且newdir必須不存在
 
重命名文件(目錄)
os.rename("oldname","newname") 文件或目錄都是使用這條命令
 
移動文件(目錄)
shutil.move("oldpos","newpos")
 
刪除文件
os.remove("file")
 
刪除目錄
os.rmdir("dir")只能刪除空目錄
shutil.rmtree("dir") 空目錄、有內容的目錄均可以刪
 
轉換目錄
os.chdir("path") 換路徑
  
ps: 文件操做時,經常配合正則表達式:
img_dir = img_dir.replace('\\','/')

3.5 模塊安裝方式

- pip包管理器
    - 源碼安裝
        - 下載->解壓->cd 到對應路徑
        - python setup.py build
        - python setup.py install

************************* 四.面向對象*************************

4.1 super原理

************************* 五.網絡編程*************************

5.1 OSI 7層協議

應/表/會/傳/網/數/物
http://www.cnblogs.com/smile233/p/8507791.html

5.2 三次握手、四次揮手

http://www.cnblogs.com/liuxiaoming/archive/2013/04/27/3047803.html

5.3 TCP和UDP

http://www.cnblogs.com/gaopeng527/p/5255827.html

************************* 六.併發編程*************************

6.1 進程、線程、協程區別

進程:正在執行的一個程序或者一個任務,而執行任務的是cpu每一個進程都有本身的獨立內存空間,不一樣進程經過進程間通訊來通訊。因爲進程比較重量,佔據獨立的內存,因此上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,相比線程數據相對比較穩定安全。

線程:線程是進程的一個實體,是CPU調度和分派的基本單位線程間通訊主要經過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。

協程:是單線程下的併發,或協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。
  優勢:
    1. 協程的切換開銷更小,屬於程序級別的切換,操做系統徹底感知不到,於是更加輕量級
    2. 單線程內就能夠實現併發的效果,最大限度地利用cpu
  缺點:
    一、io阻塞
    二、沒法利用多核

一、進程多與線程比較

  1) 地址空間:線程是進程內的一個執行單元,進程內至少有一個線程,它們共享進程的地址空間,而進程有本身獨立的地址空間
  2) 資源擁有:進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源
  3) 線程是處理器調度的基本單位,但進程不是
  4) 兩者都可併發執行
  5) 每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口,可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制

二、協程多與線程進行比較

  1)一個線程能夠多個協程,一個進程也能夠單獨擁有多個協程,這樣python中則能使用多核CPU
  2) 線程進程都是同步機制,而協程則是異步
  3) 協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態
區別:http://www.cnblogs.com/lxmhhy/p/6041001.html
總結:http://www.cnblogs.com/suoning/p/5599030.html
https://blog.csdn.net/Blateyang/article/details/78088851

6.2 GIL鎖

https://blog.csdn.net/bitcarmanlee/article/details/51577014

6.3 進程池線程池

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

http://www.cnblogs.com/haiyan123/p/7461294.html
http://www.cnblogs.com/wanghzh/p/5607067.html

************************* 七.數據庫*************************

7.1 引擎

MyISAM 適合於一些須要大量查詢的應用,但其對於有大量寫操做並非很好。甚至你只是須要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都沒法操做直到讀操做完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持「行鎖」 ,因而在寫操做比較多的時候,會更優秀。而且,他還支持更多的高級應用,好比:事務。
- innodb
        - 事務
        - 行鎖/表鎖
            - 表鎖:
                select * from tb for update;
            - 行鎖:
                select id,name from tb where id=2 for update ;
- myisam
    - 全文索引
    - 快
    - 表鎖

7.2 數據庫查表練習

習題:http://www.cnblogs.com/xuyaping/p/7106348.html
基礎:http://www.cnblogs.com/xuyaping/p/6945871.html
     http://www.cnblogs.com/xuyaping/p/6953288.html

7.3 索引相關

1.索引: B+/哈希索引   =》 查找速度快;更新速度慢
    單列:
        - 普通索引:加速查找
        - 惟一索引: 加速查找 + 約束:不能重複
        -     主鍵: 加速查找 + 約束:不能重複 + 不能爲空
    多列:
        - 聯合索引
        - 聯合惟一索引
        PS:遵循最左前綴的規則
    其餘詞語:
        - 索引合併,利用多個單例索引查詢;
        - 覆蓋索引,在索引表中就能將想要的數據查詢到;

        
2.建立了索引,應該如何命中索引?  那種狀況下,建立了沒法命中索引?
    - like '%xx'
        select * from tb1 where name like '%cn';
    - 使用函數
        select * from tb1 where reverse(name) = 'wupeiqi';
    - or
        select * from tb1 where nid = 1 or email = 'seven@live.com';
        特別的:當or條件中有未創建索引的列才失效,如下會走索引
                select * from tb1 where nid = 1 or name = 'seven';
                select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
    - 類型不一致
        若是列是字符串類型,傳入條件是必須用引號引發來,否則...
        select * from tb1 where name = 999;
    - !=
        select * from tb1 where name != 'alex'
        特別的:若是是主鍵,則仍是會走索引
            select * from tb1 where nid != 123
    - >
        select * from tb1 where name > 'alex'
        特別的:若是是主鍵或索引是整數類型,則仍是會走索引
            select * from tb1 where nid > 123
            select * from tb1 where num > 123
    - order by
        select email from tb1 order by name desc;
        當根據索引排序時候,選擇的映射若是不是索引,則不走索引
        特別的:若是對主鍵排序,則仍是走索引:
            select * from tb1 order by nid desc;

3.組合索引最左前綴
    若是組合索引爲:(name,email)
    name and email       -- 使用索引
    name                 -- 使用索引
    email                -- 不使用索引

7.4 慢日誌等

- 如何開啓慢日誌查詢?
    slow_query_log = ON                                是否開啓慢日誌記錄
    long_query_time = 2                                  時間限制,超過此時間,則記錄
    slow_query_log_file = /usr/slow.log           日誌文件
    log_queries_not_using_indexes = ON       爲使用索引的搜索是否記錄
    
- 執行計劃
    explain select * from tb;
    
- 導出現有數據庫數據:
        mysqldump -u用戶名 -p密碼 數據庫名稱 >導出文件路徑         # 結構+數據
        mysqldump -u用戶名 -p密碼 -d 數據庫名稱 >導出文件路徑     # 結構 
- 導入現有數據庫數據:
        mysqldump -uroot -p密碼 數據庫名稱 < 文件路徑

7.5 數據庫最大歷史頁數查詢解決方案

問:查詢頁數越大效率越低,如何優化?
select * form tb limit 10 offset 0
select * form tb limit 10 offset 10
select * form tb limit 10 offset 20
select * form tb limit 10 offset 30
...
select * form tb limit 10 offset 3000000
            
答案一:
    先查主鍵,在分頁。
    select * from tb where id in (
        select id from tb where limit 10 offset 30
    )
答案二:
    按照也無需求是否能夠設置只讓用戶看200頁
    
答案三:
    記錄當前頁  數據ID最大值和最小值
    在翻頁時,根據條件先進行篩選;篩選完畢以後,再根據limit offset 查詢。
    
    select * from (select * from tb where id > 22222222) as B limit 10 offset 0
    
    若是用戶本身修改頁碼,也可能致使慢;此時對url種的頁碼進行加密(rest framework )

************************* redis *************************

7.6 redis 鏈接和基本操做

- 鏈接
        - 直接鏈接:
            import redis 
            r = redis.Redis(host='10.211.55.4', port=6379)
            r.set('foo', 'Bar')
            print r.get('foo')
        - 鏈接池:
            import redis
            pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
             
            r = redis.Redis(connection_pool=pool)
            r.set('foo', 'Bar')
            print r.get('foo')
    
    - 5大數據類型
        - 字符串 "abc"
            - set('k1','123',ex=10)
            - get 
            - mset
            - mget
            - incr
            - 超時時間:
                import redis 
                r = redis.Redis(host='10.211.55.4', port=6379)
                r.set('foo', 'Bar',ex=10)
        - 字典 {'k1':'v1'}
            - hset(name, key, value)
            - hmset(name, mapping)
            - hget(name,key)
            - 超時時間(字典,列表、集合、有序結合相同):
                import redis 
                conn = redis.Redis(host='10.211.55.4', port=6379)
                conn.hset('n1', 'k1','123123')
                conn.expire('n1',10)
            - 若是一個字典在redis中保存了10w個值,我須要將全部值所有循環並顯示,請問如何實現?
                
                for item in r.hscan_iter('k2',count=100):
                    print item
                    
        - 列表 [11,22,33]
            - lpush
            - rpush
            - lpop
            - blpop
            - rpop
            - brpop
            - llen
            - lrange
            - 若是一個列表在redis中保存了10w個值,我須要將全部值所有循環並顯示,請問如何實現?
                def list_scan_iter(name,count=3):
                    start = 0
                    while True:
                        result = conn.lrange(name, start, start+count-1)
                        start += count
                        if not result:
                            break
                        for item in result:
                            yield item

                for val in list_scan_iter('num_list'):
                    print(val)
                
        - 集合 {'fry','bender','leela'}
            
        - 有序集合 {('fry',59),('bender',100),('leela',1)}
    
    - 公共操做:
        - delete(*names)        # 根據name刪除redis中的任意數據類型
        - keys(pattern='*')     # 根據* ?等通配符匹配獲取redis的name
        - expire(name ,time)    # 設置超時時間
        ...

    http://www.cnblogs.com/melonjiang/p/5342383.html
    http://www.cnblogs.com/melonjiang/p/5342505.html

7.7 redis 事務

import redis

    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)

    conn = redis.Redis(connection_pool=pool)

    # pipe = r.pipeline(transaction=False)
    pipe = conn.pipeline(transaction=True)
    # 開始事務
    pipe.multi()

    pipe.set('name', 'bendere')
    pipe.set('role', 'sb')

    # 提交
    pipe.execute()
    
    注意:諮詢是否當前分佈式redis是否支持事務

7.8 redis WATCH命令

在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設咱們經過WATCH命令在事務執行以前監控了多個Keys,假若在WATCH以後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知調用者事務執行失敗。
    
    面試題:你如何控制剩餘的數量不會出問題?
        - 經過redis的watch實現
            import redis
            conn = redis.Redis(host='127.0.0.1',port=6379)

            # conn.set('count',1000)
            val = conn.get('count')
            print(val)

            with conn.pipeline(transaction=True) as pipe:

                # 先監視,本身的值沒有被修改過
                conn.watch('count')

                # 事務開始
                pipe.multi()
                old_count = conn.get('count')
                count = int(old_count)
                print('如今剩餘的商品有:%s',count)
                input("問媳婦讓不讓買?")
                pipe.set('count', count - 1)

                # 執行,把全部命令一次性推送過去
                pipe.execute()
        - 數據庫的鎖

7.9 redis 發佈和訂閱

發佈者:
        import redis

        conn = redis.Redis(host='127.0.0.1',port=6379)
        conn.publish('104.9MH', "hahaha")
    訂閱者:
        import redis

        conn = redis.Redis(host='127.0.0.1',port=6379)
        pub = conn.pubsub()
        pub.subscribe('104.9MH')

        while True:
            msg= pub.parse_response()
            print(msg)

7.10 redis 應用場景?

- 計數器
    - 排行榜
    - 熱門商品
    - 購物車信息
    - rest api中訪問頻率控制
    - 基於flask、websocket實現的投票系統(redis作消息隊列)
    - 配合django作緩存,經常使用且不易修改的數據放進來(博客) 
    - Session 
        - 緩存配置文件
        - session配置文件中指定使用緩存    
    - scrapy中
        - 去重規則
        - 調度器:先進先出、後進先出、優先級隊列
        - pipelines 
        - 起始URL

7.11 redis 主從複製的做用?

目的是對redis作高可用,爲每個redis實例建立一個備份稱爲slave,讓主和備之間進行數據同步,save/bsave。
        主:寫
        從:讀
    優勢:
        - 性能提升,從分擔讀的壓力。
        - 高可用,一旦主redis掛了,從能夠直接代替。
    
    存在問題:當主掛了以後,須要人爲手工將從變成主。
    注意: 
        - slave設置只讀 
    
    從的配置文件添加如下記錄,便可:
        slaveof 1.1.1.1 3306

7.12 redis的sentinel是什麼?

幫助咱們自動在主從之間進行切換
    檢測主從中 主是否掛掉,且超過一半的sentinel檢測到掛了以後才進行進行切換。
    若是主修復好了,再次啓動時候,會變成從。

    啓動主redis:
    redis-server /etc/redis-6379.conf  啓動主redis
    redis-server /etc/redis-6380.conf  啓動從redis
        
    在linux中:
        找到 /etc/redis-sentinel-8001.conf  配置文件,在內部:
            - 哨兵的端口 port = 8001
            - 主redis的IP,哨兵個數的一半/1
        
        找到 /etc/redis-sentinel-8002.conf  配置文件,在內部:
            - 哨兵的端口 port = 8002
            - 主redis的IP, 1 
    
        啓動兩個哨兵

7.13 redis的cluster是什麼?

redis集羣、分片、分佈式redis     
    redis-py-cluster
    集羣方案:
        - redis cluster 官方提供的集羣方案。
        - codis,豌豆莢技術團隊。
        - tweproxy,Twiter技術團隊。
    redis cluster的原理?
        - 基於分片來完成。
        - redis將全部能放置數據的地方建立了 16384 個哈希槽。
        - 若是設置集羣的話,就能夠爲每一個實例分配哈希槽:
            - 192.168.1.20【0-5000】
            - 192.168.1.21【5001-10000】
            - 192.168.1.22【10001-16384】
        - 之後想要在redis中寫值時,
            set k1 123 
          將k1經過crc16的算法,將k1轉換成一個數字。而後再將該數字和16384求餘,若是獲得的餘數 3000,那麼就將該值寫入到 192.168.1.20 實例中。

7.14 redis是否能夠作持久化?

RDB:每隔一段時間對redis進行一次持久化。
         - 缺點:數據不完整
         - 優勢:速度快
    AOF:把全部命令保存起來,若是想到從新生成到redis,那麼就要把命令從新執行一次。
         - 缺點:速度慢,文件比較大
         - 優勢:數據完整

7.15 redis的過時策略(數據淘汰策略)

voltile-lru:    從已設置過時時間的數據集(server.db[i].expires)中挑選最近頻率最少數據淘汰
    volatile-ttl:   從已設置過時時間的數據集(server.db[i].expires)中挑選將要過時的數據淘汰
    volatile-random:從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據淘汰

    
    allkeys-lru:       從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
    allkeys-random:    從數據集(server.db[i].dict)中任意選擇數據淘汰
    no-enviction(驅逐):禁止驅逐數據

7.16 redis的分佈式鎖實現

- 寫值並設置超時時間
    - 超過一半的redis實例設置成功,就表示加鎖完成。
    - 使用:安裝redlock-py 
        from redlock import Redlock

        dlm = Redlock(
            [
                {"host": "localhost", "port": 6379, "db": 0},
                {"host": "localhost", "port": 6379, "db": 0},
                {"host": "localhost", "port": 6379, "db": 0},
            ]
        )

        # 加鎖,acquire
        my_lock = dlm.lock("my_resource_name",10000)
        if  my_lock:
            # J進行操做
            # 解鎖,release
            dlm.unlock(my_lock)
        else:
            print('獲取鎖失敗')
    
    http://www.redis.cn/

7.17 redis 如何提升併發

目前大家公司項目1000用戶,QPS=1000 ,若是用戶猛增10000w?
    項目如何提升的併發?
    
    1. 數據庫讀寫分離
    2. 設置緩存
    3. 負載均衡

7.18 其餘彙總

redis常見面試題
        http://www.cnblogs.com/Survivalist/p/8119891.html
        http://www.cnblogs.com/wupeiqi/articles/5132791.html

************************* 八.前端*************************

8.1 響應式佈局

@media (min-width: 768px){
    .pg-header{
        background-color: green;
    }
}
@media (min-width: 992px){
    .pg-header{
        background-color: pink;
    }
}

8.2 跨域實現(JSONP&CORS)

AJAX即「Asynchronous Javascript And XML」(異步JavaScript和XML),是指一種建立交互式網頁應用的網頁開發技術
AJAX能夠在不從新加載整個頁面的狀況下,與服務器交換數據
Ajax的核心是XMLHttpRequest對象(XHR)。XHR爲向服務器發送請求和解析服務器響應提供了接口。可以以異步方式從服務器獲取新數據。

1.JSONP
JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問。jsonp只能經過get方式進行跨域請求
2.CORS
http://www.cnblogs.com/aylin/p/5732242.html

************************* 九.Django*************************

9.1 你瞭解哪些web框架及其區別

django:大而全的全的框架,重武器;內置不少組件:ORM、admin、Form、ModelForm、中間件、信號、緩存、csrf等
flask: 微型框架、可擴展強,若是開發簡單程序使用flask比較快速,若是實現負責功能就須要引入一些組件:flask-session/flask-SQLAlchemy/wtforms/flask-migrate/flask-script/blinker

這兩個框架都是基於wsgi協議實現的,默認使用的wsgi模塊不同。
還有一個顯著的特色,他們處理請求的方式不一樣:
django: 經過將請求封裝成Request對象,再經過參數進行傳遞。
flask:經過上下文管理實現。

9.2 Django請求的生命週期

a. wsgi, 建立socket服務端,用於接收用戶請求並對請求進行初次封裝。
b. 中間件,對全部請求到來以前,響應以前定製一些操做。
c. 路由匹配,在url和視圖函數對應關係中,根據當前請求url找到相應的函數。
d. 執行視圖函數,業務處理【經過ORM去數據庫中獲取數據,再去拿到模板,而後將數據和模板進行渲染】
e. 再通過全部中間件。
f. 經過wsgi將響應返回給用戶。

9.3 什麼是WSGI?

WSGI : Web Server Gateway Interface web 服務器網關接口
是web服務網關接口,是一套協議。如下模塊實現了wsgi協議:
                - wsgiref
                - werkzurg
                - uwsgi
本質上就是編寫了socket服務端,用來監聽用戶請求,若是有請求到來,則將請求進行一次封裝,而後將【請求】交給 web框架來進行下一步處理。
from wsgiref.simple_server import make_server

def run_server(environ, start_response):
    """
    environ: 封裝了請求相關的數據
    start_response:用於設置響應頭相關數據
    """
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
 
 
if __name__ == '__main__':
    httpd = make_server('', 8000, run_server)
    httpd.serve_forever()

9.4 中間件

中間件的做用?對全部的請求進行批量處理,在視圖函數執行先後進行自定義操做。
中間件的應用?cors跨域/用戶登陸校驗/權限處理/CSRF/session/緩存
中間件中方法?5個方法,分別是:
    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)
    1 請求先執行全部中間件的process_request,而後作路由匹配,找到函數不執行。
    2 再執行全部的process_view,在執行視圖函數。
    3 再執行process_response
    4 若是程序報錯執行process_exception
    5 若是程序有render方法則執行process_template_response
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

9.5 路由視圖

urlpatterns = [url(正則表達式, views視圖函數,參數,別名),]

9.6 視圖

1 fbv方式請求的過程
用戶發送url請求,Django會依次遍歷路由映射表中的全部記錄,一旦路由映射表其中的一條匹配成功了,
就執行視圖函數中對應的函數名,這是fbv的執行流程

2 cbv方式請求的過程
當服務端使用cbv模式的時候,用戶發給服務端的請求包含url和method,這兩個信息都是字符串類型

服務端經過路由映射表匹配成功後會自動去找dispatch方法,而後Django會經過dispatch反射的方式找到類中對應的方法並執行

類中的方法執行完畢以後,會把客戶端想要的數據返回給dispatch方法,由dispatch方法把數據返回經客戶端

把上面的例子中的視圖函數修改爲以下:
from django.views import View

class CBV(View):
    def dispatch(self, request, *args, **kwargs):
        print("dispatch......")
        res=super(CBV,self).dispatch(request,*args,**kwargs)
        return res

    def get(self,request):
        return render(request, "cbv.html")

    def post(self,request):
        return HttpResponse("cbv.get")

3 FBV和CBV的區別?
    - 沒什麼區別,由於他們的本質都是函數。CBV的.as_view()返回的view函數,view函數中調用類的dispatch方法,在dispatch方法中經過反射執行get/post/delete/put等方法。
    - CBV比較簡潔,GET/POST等業務功能分別放在不一樣get/post函數中。FBV本身作判斷進行區分。

http://www.cnblogs.com/renpingsheng/p/7534897.html

9.7 csrf原理

目標:防止用戶直接向服務端發起POST請求。
方案:先發送GET請求時,將token保存到:cookie、Form表單中(隱藏的input標籤),之後再發送請求時只要攜帶過來便可。

問題:若是想後臺發送POST請求?

1).form表單提交:
    <form method="POST">
        {% csrf_token %}
        <input type='text' name='user' />
        <input type='submit' />
    </form>
    
2).ajax提交:
    $.ajax({
        url:'/index',
        type:'POST',
        data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
    })
    
    前提:引入jquery + 引入jquery.cookie 
    $.ajax({
        url: 'xx',
        type:'POST',
        data:{name:'oldboyedu'},
        headers:{
            X-CSRFToken: $.cookie('csrftoken')
        },
        dataType:'json', // arg = JSON.parse('{"k1":123}')
        success:function(arg){
            
        }
    })
    
    優化方案:
        <body>
            <input type="button" onclick="Do1();"  value="Do it"/>
            <input type="button" onclick="Do2();"  value="Do it"/>

            <script src="/static/jquery-3.3.1.min.js"></script>
            <script src="/static/jquery.cookie.js"></script>
            <script>
                $.ajaxSetup({
                    beforeSend: function(xhr, settings) {
                        xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
                    }
                });

                 function Do1(){
                    $.ajax({
                        url:"/index/",
                        data:{id:1},
                        type:'POST',
                        success:function(data){
                            console.log(data);
                        }
                    });
                }

                 function Do2(){
                    $.ajax({
                        url:"/index/",
                        data:{id:1},
                        type:'POST',
                        success:function(data){
                            console.log(data);
                        }
                    });
                }
            </script>
        </body>
        

3).爬蟲:
    reqeusts.post()

9.8 如何動態獲取數據庫字段

方式一:重寫構造方法,在構造方法中從新去數據庫獲取值
class UserForm(Form):
    name = fields.CharField(label='用戶名',max_length=32)
    email = fields.EmailField(label='郵箱')
    ut_id = fields.ChoiceField(
        # choices=[(1,'二B用戶'),(2,'山炮用戶')]
        choices=[]
    )

    def __init__(self,*args,**kwargs):
        super(UserForm,self).__init__(*args,**kwargs)

        self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title')
方式二: ModelChoiceField字段
from django.forms import Form
from django.forms import fields
from django.forms.models import ModelChoiceField
class UserForm(Form):
    name = fields.CharField(label='用戶名',max_length=32)
    email = fields.EmailField(label='郵箱')
    ut_id = ModelChoiceField(queryset=models.UserType.objects.all())    

依賴:
    class UserType(models.Model):
        title = models.CharField(max_length=32)

        def __str__(self):
            return self.title

9.9 CBV裝飾器注意事項

- 裝飾器
from django.views import View
from django.utils.decorators import method_decorator

def auth(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

class UserView(View):                       
    @method_decorator(auth)
    def get(self,request,*args,**kwargs):
        return HttpResponse('...')

- csrf的裝飾器要加到dispath
    from django.views import View
    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt,csrf_protect


    class UserView(View):
        @method_decorator(csrf_exempt)
        def dispatch(self, request, *args, **kwargs):
            return HttpResponse('...')
        
或
    from django.views import View
    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt,csrf_protect

    @method_decorator(csrf_exempt,name='dispatch')
    class UserView(View):
        def dispatch(self, request, *args, **kwargs):
            return HttpResponse('...')

9.10 Django中form組件的做用

- 對用戶請求的數據進行校驗
- 生成HTML標籤

PS:
    - form對象是一個可迭代對象。

9.11 如何在一個項目中使用多數據庫

python manage.py makemigraions

python manage.py migrate app名稱 --databse=配置文件數據名稱的別名

手動操做:
    models.UserType.objects.using('db1').create(title='普通用戶')
    result = models.UserType.objects.all().using('default')
    
自動操做:
    根目錄下db_router.py :
    class Router1:
        def db_for_read(self, model, **hints):
            """
            Attempts to read auth models go to auth_db.
            """
            return 'db1'

        def db_for_write(self, model, **hints):
            """
            Attempts to write auth models go to auth_db.
            """
            return 'default'

    配置:
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
            },
            'db1': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
            },
        }
        DATABASE_ROUTERS = ['db_router.Router1',]
        
    使用:
        models.UserType.objects.create(title='VVIP')

        result = models.UserType.objects.all()
        print(result)
                            
    補充:粒度更細
        class Router1:
            def db_for_read(self, model, **hints):
                """
                Attempts to read auth models go to auth_db.
                """
                if model._meta.model_name == 'usertype':
                    return 'db1'
                else:
                    return 'default'

            def db_for_write(self, model, **hints):
                """
                Attempts to write auth models go to auth_db.
                """
                return 'default'

9.12 如何在不一樣app中使用不一樣數據庫

# 第一步:
    python manage.py makemigraions 

# 第二步:
    app01中的表在default數據庫建立
    python manage.py migrate app01 --database=default

# 第三步:
    app02中的表在db1數據庫建立
    python manage.py migrate app02 --database=db1
    
# 手動操做:
    m1.UserType.objects.using('default').create(title='VVIP')
    m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
# 自動操做:
    配置: 
        class Router1:
            def db_for_read(self, model, **hints):
                """
                Attempts to read auth models go to auth_db.
                """
                if model._meta.app_label == 'app01':
                    return 'default'
                else:
                    return 'db1'

            def db_for_write(self, model, **hints):
                """
                Attempts to write auth models go to auth_db.
                """
                if model._meta.app_label == 'app01':
                    return 'default'
                else:
                    return 'db1'

        DATABASE_ROUTERS = ['db_router.Router1',]
    
    使用: 
        m1.UserType.objects.using('default').create(title='VVIP')
        m2.Users.objects.using('db1').create(name='VVIP',email='xxx')

9.13 如何在數據庫遷移時進行讀寫約束

class Router1:
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        if db=='db1' and app_label == 'app02':
            return True
        elif db == 'default' and app_label == 'app01':
            return True
        else:
            return False
        
        # 若是返回None,那麼表示交給後續的router,若是後續沒有router,則至關於返回True
        
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

9.14 輪詢和長輪詢

- 什麼是輪詢?
    - 經過定時器讓程序每隔n秒執行一次操做。
- 什麼是長輪詢?
    - 瀏覽器向後端發起請求,後端會將請求 hang 住,最多hang 30s。
        若是一直不返回數據:則最多等待30s,緊接着用戶當即再發送請求。
        若是有數據返回:則操做數據並當即再發送請求。
    PS:後臺可使用隊列或redis的列表來hang主請求。
    
- 輪詢和長輪詢目的?
    因爲Http請求是無狀態、短鏈接因此服務端沒法向客戶端實時推送消息,
    因此,咱們就是可使用:輪詢和長輪詢去服務端獲取實時數據。


a. 實時消息推送,利用什麼技術實現?
    - 輪詢        ,優勢:簡單;                     缺點:請求次數多,服務器壓力大,消息延遲。
    - 長輪詢    ,優勢:實時接收數據,兼容性好;     缺點:請求次數比原來少,可是相對來也很多。
    - websocket ,優勢:代碼簡單,再也不反覆建立鏈接。 缺點:兼容性。 
b. 在框架中使用WebSocket:
    - django,channel
    - flask,gevent-websocket 
    - tornado,內置

9.15 websocket

websocket是一套相似於http的協議。
擴展:
         http協議:\r\n分割、請求頭和請求體\r\n分割、無狀態、短鏈接。
    websocket協議:\r\n分割、建立鏈接後不斷開、 驗證+數據加密;
    
websocket本質:
    - 就是一個建立鏈接後不斷開的socket
    - 當鏈接成功以後:
        - 客戶端(瀏覽器)會自動向服務端發送消息,包含: Sec-WebSocket-Key: iyRe1KMHi4S4QXzcoboMmw==
        - 服務端接收以後,會對於該數據進行加密:
            base64(sha1(swk + magic_string))
            
        - 構造響應頭:
            HTTP/1.1 101 Switching Protocols\r\n
            Upgrade:websocket\r\n
            Connection: Upgrade\r\n
            Sec-WebSocket-Accept: 加密後的值\r\n
            WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n

        - 發給客戶端(瀏覽器)
    - 創建:雙工通道,接下來就能夠進行收發數據
        - 發送的數據是加密,解密,根據payload_len的值進行處理:
            - payload_len <=125
            - payload_len ==126
            - payload_len ==127
        - 獲取內容:
            - mask_key 
            - 數據 
            根據mask_key和數據進行位運算,就能夠把值解析出來。
            

面試:
    a. 什麼是websocket?
        websocket是給瀏覽器新建一套協議。協議規定:瀏覽器和服務端鏈接以後不斷開,以此能夠完成:服務端向客戶端主動推送消息。
        websocket協議額外作的一些前天操做:
            - 握手,鏈接前進行校驗
            - 發送數據加密
        
    b. websocket本質
        - socket
        - 握手,魔法字符串+加密 
        - 加密,payload_len=127/126/<=125 -> mask key

9.16 自定義模板方法

- simple_tag    
    - filter
        http://www.cnblogs.com/SunsetSunrise/p/7680491.html
    - inclusion_tags
        http://www.cnblogs.com/iyouyue/p/8626515.html

9.17 其餘彙總

1) django全部組件?          
        分頁
        session
        form 
        modelform  利用model生成form
        auth        
        
    2) MTV和MVC?
        
        MVC: model view controller          
        MTV: model tempalte view    
        
    3) 模板 
        - 索引: {{v.0}}
        - 方法執行: {% for item in dic.items %} {%endfor%}
        - 模板繼承
        - 自定義方法:
                - simple_tag
                - inclusion_tags
                - filter


    4) Form和ModelForm的做用?區別?應用場景?
        - 做用:
            - 對用戶請求數據格式進行校驗
            - 自動生成HTML標籤
        - 區別:
            - Form,字段須要本身手寫。
                class Form(Form):
                    xx = fields.CharField(.)
                    xx = fields.CharField(.)
                    xx = fields.CharField(.)
                    xx = fields.CharField(.)
            - ModelForm,能夠經過Meta進行定義
                class MForm(ModelForm):
                    class Meta:
                        fields = "__all__"
                        model = UserInfo                            
        - 應用:只要是客戶端向服務端發送表單數據時,均可以進行使用,如:用戶登陸註冊
        
    5) 爲何要用緩存?
        將經常使用且不太頻繁修改的數據放入緩存。
        之後用戶再來訪問,先去緩存查看是否存在,若是有就返回
        不然,去數據庫中獲取並返回給用戶(再加入到緩存,以便下次訪問)
        
    6) django內部支持哪些緩存?
        Django中提供了6種緩存方式:
            開發調試(不加緩存)
            內存
            文件
            數據庫
            Memcache緩存(python-memcached模塊)
            Memcache緩存(pylibmc模塊)
                        
        安裝第三方組件支持redis:
            django-redis組件 

    7) 設置緩存
        - 全站緩存
        - 視圖函數緩存
        - 局部模板緩存
        
    8) django的信號做用?
        django的信號其實就是django內部爲開發者預留的一些自定製功能的鉤子。
        只要在某個信號中註冊了函數,那麼django內部執行的過程當中就會自動觸發註冊在信號中的函數。
        如: 
                
            pre_init                    # django的modal執行其構造方法前,自動觸發
            post_init                   # django的modal執行其構造方法後,自動觸發
            pre_save                    # django的modal對象保存前,自動觸發
            post_save                   # django的modal對象保存後,自動觸發
            
        用信號作過什麼?
            在數據庫某些表中添加數據時,能夠進行日誌記錄。
            
    9) 序列化 
        內置:
            from django.core import serializers
            
            #queryset = [obj,obj,obj]
            ret = models.BookType.objects.all()
             
            data = serializers.serialize("json", ret)
        json:
            import json

            #ret = models.BookType.objects.all().values('caption')
            ret = models.BookType.objects.all().values_list('caption')
             
            ret=list(ret)
             
            result = json.dumps(ret)
            
            補充:
                - json.dumps(ensure_ascii=True)
                - json.dumps( cls=JSONEncoder)

    10) admin & stark組件

        - 爲公司定製更適用於本身的組件: stark組件
            
    11) ContentType的做用?以及應用場景?
        
        contenttype是django的一個組件(app),爲咱們找到django程序中全部app中的全部表並添加到記錄中。
        
        可使用他再加上表中的兩個字段實現:一張表和N張表建立FK關係。
            - 字段:表名稱
            - 字段:數據行ID
            
        應用:路飛表結構優惠券和專題課和學位課關聯。
        http://www.cnblogs.com/iyouyue/p/8810464.html
        

    12) 冪等性是什麼意思?

    就是屢次相同的操做,結果都不變
    好比,讀取一個文件,1次和10次,讀出來的內容應該是同樣的。
        
        
    13) 遠程數據交互的三種方式:
        - webservice, webservice是在rest api普遍引用以前你們使用的一個跨平臺交互的接口。
        - restfull api
        - RPC
        
        擴展:WCF他是建立了一個雙工通道。
        
    14) HTTPS
        Http: 80端
        https: 443端口
        
        - 自定義證書 
            - 服務端:建立一對證書
            - 客戶端:必須攜帶證書
        - 購買證書
            - 服務端: 建立一對證書……
            - 客戶端: 去機構獲取證書,數據加密後發給我們的服務單
            - 證書機構:公鑰給改機構

************************* 十.Flask*************************

10.1 django和flask區別?

(1)Flask

Flask確實很「輕」,不愧是Micro Framework,從Django轉向Flask的開發者必定會如此感慨,除非兩者均爲深刻使用過
Flask自由、靈活,可擴展性強,第三方庫的選擇面廣,開發時能夠結合本身最喜歡用的輪子,也能結合最流行最強大的Python庫
入門簡單,即使沒有多少web開發經驗,也能很快作出網站
很是適用於小型網站
很是適用於開發web服務的API
開發大型網站無壓力,但代碼架構須要本身設計,開發成本取決於開發者的能力和經驗
各方面性能均等於或優於Django
Django自帶的或第三方的好評如潮的功能,Flask上總會找到與之相似第三方庫
Flask靈活開發,Python高手基本都會喜歡Flask,但對Django卻可能褒貶不一
Flask與關係型數據庫的配合使用不弱於Django,而其與NoSQL數據庫的配合遠遠優於Django
Flask比Django更加Pythonic,與Python的philosophy更加吻合

(2)Django
Django過重了,除了web框架,自帶ORM和模板引擎,靈活和自由度不夠高
Django能開發小應用,但總會有「殺雞焉用牛刀」的感受
Django的自帶ORM很是優秀,綜合評價略高於SQLAlchemy
Django自帶的模板引擎簡單好用,但其強大程度和綜合評價略低於Jinja
Django自帶ORM也使Django與關係型數據庫耦合度太高,若是想使用MongoDB等NoSQL數據,須要選取合適的第三方庫,且總感受Django+SQL纔是天生一對的搭配,Django+NoSQL砍掉了Django的半壁江山
Django目前支持Jinja等非官方模板引擎
Django自帶的數據庫管理app好評如潮
Django很是適合企業級網站的開發:快速、靠譜、穩定
Django成熟、穩定、完善,但相比於Flask,Django的總體生態相對封閉
Django是Python web框架的先驅,用戶多,第三方庫最豐富,最好的Python庫,若是不能直接用到Django中,也必定能找到與之對應的移植
Django上手也比較容易,開發文檔詳細、完善,相關資料豐富

10.2 flask組件

內置:
    - 配置
        - 路由
        - 視圖
        - 模板
        - session
        - 閃現
        - 藍圖
        - 中間件
        - 特殊裝飾器
第三方:
    - Flask組件:
        - flask-session 
        - flask-SQLAlchemy
        - flask-migrate 
        - flask-script 
        - blinker 
    - 公共組件:
        - wtforms
        - dbutile
        - sqlalchemy 
    - 自定義Flask組件
        - auth ,參考flask-login組件

10.3 上下文管理流程主要涉及到了那些類?並描述類主要做用?

每次有請求過來的時候,flask 會先建立當前線程或者進程須要處理的兩個重要上下文對象,把它們保存到隔離的棧裏面,這樣視圖函數進行處理的時候就能直接從棧上獲取這些信息。
參考連接:http://python.jobbole.com/87398/

10.4 爲何實用LocalStack對Local對象進行操做?

目的是想要將local中的值維護成一個棧,例如:在多app應用中編寫離線腳本時,能夠實用上。
from m_app import app01,app02
from flask import current_app
"""
{
    1231: {
        stack: [app01,app02,]
    }
}

"""

with app01.app_context():
    print(current_app)
    with app02.app_context():
        print(current_app)
    print(current_app)

10.5 threading.local 以及做用

a. threading.local 
    做用:爲每一個線程開闢一塊空間進行數據存儲。

    問題:本身經過字典建立一個相似於threading.local的東西。
        storage={
            4740:{val:0},
            4732:{val:1},
            4731:{val:3},
            ...
        }

    b. 自定義Local對象
    做用:爲每一個線程(協程)開闢一塊空間進行數據存儲。

        try:
            from greenlet import getcurrent as get_ident
        except Exception as e:
            from threading import get_ident

        from threading import Thread
        import time

        class Local(object):

            def __init__(self):
                object.__setattr__(self,'storage',{})

            def __setattr__(self, k, v):
                ident = get_ident()
                if ident in self.storage:
                    self.storage[ident][k] = v
                else:
                    self.storage[ident] = {k: v}

            def __getattr__(self, k):
                ident = get_ident()
                return self.storage[ident][k]

        obj = Local()

        def task(arg):
            obj.val = arg
            obj.xxx = arg
            print(obj.val)

        for i in range(10):
            t = Thread(target=task,args=(i,))
            t.start()

10.6 SQLAlchemy中的 session 的建立有幾種方式?

- 直接建立Session對象
        engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
        Session = sessionmaker(bind=engine)


        def task(arg):
            session = Session()

            obj1 = Users(name="alex1")
            session.add(obj1)

            session.commit()


        for i in range(10):
            t = threading.Thread(target=task, args=(i,))
            t.start()
                        
    - 基於scoped_session(Flask-SQLAlchemy中的鏈接默認使用該方法)
        engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
        Session = sessionmaker(bind=engine)
        
        session = scoped_session(Session)

        def task(arg):

            obj1 = Users(name="alex1")
            session.add(obj1)

            session.commit()


        for i in range(10):
            t = threading.Thread(target=task, args=(i,))
            t.start()

    https://www.cnblogs.com/wupeiqi/articles/8259356.html

10.7 其餘彙總

1)flask源碼解析之簡介兩個依賴
        (werkzeug和jinja2)
    2)Flask中的g的做用?
    g對象是在一個請求中共享變量,不一樣的請求對應的是不一樣的g對象。

************************* 十一.ORM*************************

11.1 增刪改查

增   
    1)  models.UserInfo.objects.create()    
    2)  obj = models.UserInfo(name='xx')
        obj.save()
    3)  models.UserInfo.objects.bulk_create([models.UserInfo(name='xx'),models.UserInfo(name='xx')])
    刪
    1)  models.UserInfo.objects.all().delete()
    改
    1)  models.UserInfo.objects.all().update(age=18)
    2)  b = Book.objects.get(id=5)  # 注意這裏是get方法,只能拿1個,不能用filter。
        b.price = 300
        b.save()
    3)  models.UserInfo.objects.all().update(salary=F('salary')+1000)
    查
    1)  見下文詳細介紹

11.2 13條經常使用查詢方法

返回QuerySet對象的方法有:
        all()
        filter()
        exclude()
        order_by()
        reverse()
        distinct()
    特殊的QuerySet:
        values()       返回一個可迭代的字典序列
        values_list() 返回一個可迭代的元組序列
    返回具體對象的:
        get()
        first()
        last()
    返回布爾值的方法有:
        exists()
    返回數字的方法有:
        count()

11.3 雙下劃線方法

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值 
    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於十一、2二、33的數據
    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
     
    models.Tb1.objects.filter(name__contains="ven")  # 獲取name字段包含"ven"的
    models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
     
    models.Tb1.objects.filter(id__range=[1, 3])      # id範圍是1到3的,等價於SQL的bettwen and
     
    相似的還有:startswith,istartswith, endswith, iendswith 

    date字段還能夠:
    models.Class.objects.filter(first_day__year=2017)

11.4 執行原生SQL的三種方式

1.使用execute執行自定義SQL
    # from django.db import connection, connections
    # cursor = connection.cursor()  # cursor = connections['default'].cursor()
    # cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    # row = cursor.fetchone()

    2.使用extra方法 

    # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

    3.使用raw方法
      解釋:執行原始sql並返回模型
      說明:依賴model多用於查詢
      用法:
        book = Book.objects.raw("select * from hello_book")
        for item in book:
          print(item.title)
    https://www.cnblogs.com/413xiaol/p/6504856.html

11.5 外鍵別名之related參數(方便反向查找)

設置了related_query_name 反向查找時就是obj.別名_set.all()保留了_set
    from django.db import models

    class Userinfo(models.Model):
        nikename=models.CharField(max_length=32)
        username=models.CharField(max_length=32)
        password=models.CharField(max_length=64)
        sex=((1,'男'),(2,'女'))
        gender=models.IntegerField(choices=sex)


    '''把男女表混合在一塊兒,在代碼層面控制第三張關係表的外鍵關係  '''
       #寫到此處問題就來了,原來兩個外鍵 對應2張表 2個主鍵 能夠識別男女
       #如今兩個外鍵對應1張表  反向查找 沒法區分男女了了
       # object對象女.U2U.Userinfo.set  object對象男.U2U.Userinfo.set
       #因此要加related_query_name對 表中主鍵 加以區分
     
    class U2U(models.Model):
        b=models.ForeignKey(Userinfo,related_query_name='a')
        g=models.ForeignKey(Userinfo,related_query_name='b')
    ****************
    設置了related_name就是 反向查找時就說 obj.別名.all()  

    上面例子換成
    class U2U(models.Model):
        b=models.ForeignKey(Userinfo,related_name='a')
        g=models.ForeignKey(Userinfo,related_name='b')
        #查找方法 
        # 男 obj.a.all()
        # 女 obj.b.all()
    http://www.cnblogs.com/heysn21/articles/8652211.html

11.6 分組函數和聚合函數

一、aggregate(*args,**kwargs)  聚合函數
    經過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每個參數都指定一個包含在字典中的返回值。即在查詢集上生成聚合。

      from django.db.models import Avg,Sum,Max,Min
        #求書籍的平均價
        ret=models.Book.objects.all().aggregate(Avg('price'))
        #{'price__avg': 145.23076923076923}

        #參與西遊記著做的做者中最老的一位做者
        ret=models.Book.objects.filter(title__icontains='西遊記').values('author__age').aggregate(Max('author__age'))
        #{'author__age__max': 518}

        #查看根哥出過得書中價格最貴一本
        ret=models.Author.objects.filter(name__contains='根').values('book__price').aggregate(Max('book__price'))
        #{'book__price__max': Decimal('234.000')}

    二、annotate(*args,**kwargs)  分組函數
        #查看每一位做者出過的書中最貴的一本(按做者名分組 values() 而後annotate 分別取每人出過的書價格最高的)
        ret=models.Book.objects.values('author__name').annotate(Max('price'))
        # < QuerySet[
        # {'author__name': '吳承恩', 'price__max': Decimal('234.000')},
        # {'author__name': '呂不韋','price__max': Decimal('234.000')},
        # {'author__name': '姜子牙', 'price__max': Decimal('123.000')},
        # {'author__name': '亞微',price__max': Decimal('123.000')},
        # {'author__name': '伯夷 ', 'price__max': Decimal('2010.000')},
        # {'author__name': '叔齊','price__max': Decimal('200.000')},
        # ] >

        #查看每本書的做者中最老的    按做者姓名分組 分別求出每組中年齡最大的
        ret=models.Book.objects.values('author__name').annotate(Max('author__age'))
        # < QuerySet[
        #  {'author__name': '吳承恩', 'author__age__max': 518},
        #  {'author__name': '金庸', 'author__age__max': 89},
        # ] >

        #查看 每一個出版社 出版的最便宜的一本書
        ret=models.Book.objects.values('publish__name').annotate(Min('price'))
        # < QuerySet[
        # {'publish__name': '清華出版社','price__min': Decimal('67.000')},
        # {'publish__name': '機械出版社','price__min': Decimal('34.000')},
        # ] >

11.7 F和Q查詢

F:
    Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做。
    修改操做也可使用F函數,好比將每一本書的價格提升30元

    例:把全部書名後面加上(初版)
        from django.db.models.functions import Concat
        from django.db.models import Value
        models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("初版"), Value(")")))
    Q:
    Q(條件1) | Q(條件2) 或
    Q(條件1) & Q(條件2) 且
    Q(條件1) & ~Q(條件2) 非

11.8 查詢優化之.select_related()和.prefetch_related()的對比

title = models.CharField(max_length=32)

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        email = models.CharField(max_length=32)
        ut = models.ForeignKey(to='UserType')
        ut = models.ForeignKey(to='UserType')
        ut = models.ForeignKey(to='UserType')
        ut = models.ForeignKey(to='UserType')
        
        
    # 1次SQL
    # select * from userinfo
    objs = UserInfo.obejcts.all()
    for item in objs:
        print(item.name)
        
    # n+1次SQL
    # select * from userinfo
    objs = UserInfo.obejcts.all()
    for item in objs:
        # select * from usertype where id = item.id 
        print(item.name,item.ut.title)

    示例1:        
    .select_related()
        # 1次SQL
        # select * from userinfo inner join usertype on userinfo.ut_id = usertype.id 
        objs = UserInfo.obejcts.all().select_related('ut')
        for item in objs:
            print(item.name,item.ut.title)
    示例2:
    .prefetch_related()
        # select * from userinfo where id <= 8
        # 計算:[1,2]
        # select * from usertype where id in [1,2]
        objs = UserInfo.obejcts.filter(id__lte=8).prefetch_related('ut')
        for obj in objs:
            print(obj.name,obj.ut.title)
    兩個函數的做用都是減小查詢次數

    示例3:
    update()和對象.save()修改方式的性能PK
        方式1
        models.Book.objects.filter(id=1).update(price=3)
        方式2
        book_obj=models.Book.objects.get(id=1)
        book_obj.price=5
        book_obj.save()
    結論:
    update() 方式1修改數據的方式,比obj.save()性能好

11.9 取消外鍵約束用 db_constraint=False

無約束:
    class UserType(models.Model):
        title = models.CharField(max_length=32)

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        email = models.CharField(max_length=32)
        # 無數據庫約束,但能夠進行鏈表
        ut = models.ForeignKey(to='UserType',db_constraint=False)

11.10 QuerySet方法大全

##################################################################
    # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
    ##################################################################

    def all(self)
        # 獲取全部的數據對象

    def filter(self, *args, **kwargs)
        # 條件查詢
        # 條件能夠是:參數,字典,Q

    def exclude(self, *args, **kwargs)
        # 條件查詢
        # 條件能夠是:參數,字典,Q

    def select_related(self, *fields)
         性能相關:表之間進行join連表操做,一次性獲取關聯的數據。
         model.tb.objects.all().select_related()
         model.tb.objects.all().select_related('外鍵字段')
         model.tb.objects.all().select_related('外鍵字段__外鍵字段')

    def prefetch_related(self, *lookups)
        性能相關:多表連表操做時速度會慢,使用其執行屢次SQL查詢在Python代碼中實現連表操做。
                # 獲取全部用戶表
                # 獲取用戶類型表where id in (用戶表中的查到的全部用戶ID)
                models.UserInfo.objects.prefetch_related('外鍵字段')



                from django.db.models import Count, Case, When, IntegerField
                Article.objects.annotate(
                    numviews=Count(Case(
                        When(readership__what_time__lt=treshold, then=1),
                        output_field=CharField(),
                    ))
                )

                students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                    models.Case(
                        models.When(absence__type='Excused', then=1),
                    default=0,
                    output_field=models.IntegerField()
                )))

    def annotate(self, *args, **kwargs)
        # 用於實現聚合group by查詢

        from django.db.models import Count, Avg, Max, Min, Sum

        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
        # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
        # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
        # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    def distinct(self, *field_names)
        # 用於distinct去重
        models.UserInfo.objects.values('nid').distinct()
        # select distinct nid from userinfo

        注:只有在PostgreSQL中才能使用distinct進行去重

    def order_by(self, *field_names)
        # 用於排序
        models.UserInfo.objects.all().order_by('-id','age')

    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
        # 構造額外的查詢條件或者映射,如:子查詢

        Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
        Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
        Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
        Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

     def reverse(self):
        # 倒序
        models.UserInfo.objects.all().order_by('-nid').reverse()
        # 注:若是存在order_by,reverse則是倒序,若是多個排序則一一倒序


     def defer(self, *fields):
        models.UserInfo.objects.defer('username','id')
        或
        models.UserInfo.objects.filter(...).defer('username','id')
        #映射中排除某列數據

     def only(self, *fields):
        #僅取某個表中的數據
         models.UserInfo.objects.only('username','id')
         或
         models.UserInfo.objects.filter(...).only('username','id')

     def using(self, alias):
         指定使用的數據庫,參數爲別名(setting中的設置)


    ##################################################
    # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
    ##################################################

    def raw(self, raw_query, params=None, translations=None, using=None):
        # 執行原生SQL
        models.UserInfo.objects.raw('select * from userinfo')

        # 若是SQL是其餘表時,必須將名字設置爲當前UserInfo對象的主鍵列名
        models.UserInfo.objects.raw('select id as nid from 其餘表')

        # 爲原生SQL設置參數
        models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

        # 將獲取的到列名轉換爲指定列名
        name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
        Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

        # 指定數據庫
        models.UserInfo.objects.raw('select * from userinfo', using="default")

        ################### 原生SQL ###################
        from django.db import connection, connections
        cursor = connection.cursor()  # cursor = connections['default'].cursor()
        cursor.execute("""SELECT * from auth_user where id = %s""", [1])
        row = cursor.fetchone() # fetchall()/fetchmany(..)


    def values(self, *fields):
        # 獲取每行數據爲字典格式

    def values_list(self, *fields, **kwargs):
        # 獲取每行數據爲元祖

    def dates(self, field_name, kind, order='ASC'):
        # 根據時間進行某一部分進行去重查找並截取指定內容
        # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
        # order只能是:"ASC"  "DESC"
        # 並獲取轉換後的時間
            - year : 年-01-01
            - month: 年-月-01
            - day  : 年-月-日

        models.DatePlus.objects.dates('ctime','day','DESC')

    def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
        # 根據時間進行某一部分進行去重查找並截取指定內容,將時間轉換爲指定時區時間
        # kind只能是 "year", "month", "day", "hour", "minute", "second"
        # order只能是:"ASC"  "DESC"
        # tzinfo時區對象
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

        """
        pip3 install pytz
        import pytz
        pytz.all_timezones
        pytz.timezone(‘Asia/Shanghai’)
        """

    def none(self):
        # 空QuerySet對象


    ####################################
    # METHODS THAT DO DATABASE QUERIES #
    ####################################

    def aggregate(self, *args, **kwargs):
       # 聚合函數,獲取字典類型聚合結果
       from django.db.models import Count, Avg, Max, Min, Sum
       result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
       ===> {'k': 3, 'n': 4}

    def count(self):
       # 獲取個數

    def get(self, *args, **kwargs):
       # 獲取單個對象

    def create(self, **kwargs):
       # 建立對象

    def bulk_create(self, objs, batch_size=None):
        # 批量插入
        # batch_size表示一次插入的個數
        objs = [
            models.DDD(name='r11'),
            models.DDD(name='r22')
        ]
        models.DDD.objects.bulk_create(objs, 10)

    def get_or_create(self, defaults=None, **kwargs):
        # 若是存在,則獲取,不然,建立
        # defaults 指定建立時,其餘字段的值
        obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

    def update_or_create(self, defaults=None, **kwargs):
        # 若是存在,則更新,不然,建立
        # defaults 指定建立時或更新時的其餘字段
        obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

    def first(self):
       # 獲取第一個

    def last(self):
       # 獲取最後一個

    def in_bulk(self, id_list=None):
       # 根據主鍵ID進行查找
       id_list = [11,21,31]
       models.DDD.objects.in_bulk(id_list)

    def delete(self):
       # 刪除

    def update(self, **kwargs):
        # 更新

    def exists(self):
        # 是否有結果

11.11 其餘彙總

若是A表的1條記錄對應B表中N條記錄成立,兩表之間就是1對多關係;在1對多關係中 A表就是主表,B表爲子表,ForeignKey字段就建在子表;
    若是B表的1條記錄也對應A表中N條記錄,兩表之間就是雙向1對多關係,也稱爲多對多關係


    char 和 varchar的區別 :
    char和varchar的共同點是存儲數據的長度,不能 超過max_length限制,不一樣點是varchar根據數據實際長度存儲,char按指定max_length()存儲數據;全部前者更節省硬盤空間
    http://www.cnblogs.com/liwenzhou/p/8660826.html

************************* 十二.rest framework*************************

12.1 談談你對restfull 規範的認識?

- restful其實就是一套編寫接口的協議,協議規定如何編寫以及如何設置返回值、狀態碼等信息。
    - 最顯著的特色:
        restful: 給用戶一個url,根據method不一樣在後端作不一樣的處理,好比:post 建立數據、get獲取數據、put和patch修改數據、delete刪除數據。
        no rest: 給調用者不少url,每一個url表明一個功能,好比:add_user/delte_user/edit_user/
    - 固然,還有協議其餘的,好比:
        - 版本,來控制讓程序有多個版本共存的狀況,版本能夠放在 url、請求頭(accept/自定義)、GET參數
        - 狀態碼,200/300/400/500
        - url中儘可能使用名詞,restful也能夠稱爲「面向資源編程」
        - api標示:
            api.YueNet.com
            www.YueNet.com/api/
-------------------------------------------------
    - https
    - 域名 
        - api.oldboy.com
        - www.oldboy.com/api
    - 版本:
        - www.oldboy.com/api/v1

    - URL資源,名詞
        - www.oldboy.com/api/v1/student

    - 請求方式:
        - GET/POST/PUT/DELETE/PATCH/OPTIONS/HEADERS/TRACE
    - 返回值:
        - www.oldboy.com/api/v1/student/    -> 結果集
        - www.oldboy.com/api/v1/student/1/  -> 單個對象
    - URL添加條件
        - www.oldboy.com/api/v1/student?page=11&size=9
    - 狀態碼:  
        - 200
        - 300
            - 301
            - 302
        - 400
            - 403
            - 404
        - 500
    - 錯誤信息
        {
            code:1000,
            meg:'xxxx'
        }
    - hyperlink
        {
            id:1
            name: ‘xiangl’,
            type: http://www.xxx.com/api/v1/type/1/
        }

12.2 rest framework都有哪些組件?

- 路由,自動幫助開發者快速爲一個視圖建立4個url
            www.oldboyedu.com/api/v1/student/$
            www.oldboyedu.com/api/v1/student(?P<format>\w+)$
            
            www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/$
            www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+)$
            
    - 版本處理
        - 問題:版本均可以放在那裏?
                - url
                - GET 
                - 請求頭 
    - 認證 
        - 問題:認證流程?

    - 權限 
        - 權限是否能夠放在中間件中?以及爲何?

    - 訪問頻率的控制
        - 匿名用戶能夠真正的防止?沒法作到真正的訪問頻率控制,只能把小白拒之門外。
          若是要封IP,使用防火牆來作。
          
        - 登陸用戶能夠經過用戶名做爲惟一標示進行控制,若是有人註冊不少帳號,也沒法防止。

    - 視圖

    - 解析器 ,根據Content-Type請求頭對請求體中的數據格式進行處理。request.data 

    - 分頁

    - 序列化
        - 序列化
            - source
            - 定義方法
        - 請求數據格式校驗

    - 渲染器

12.3 視圖中均可以繼承哪些類?

a. 繼承 APIView
        這個類屬於rest framework中頂層類,內部幫助咱們實現了只是基本功能:認證、權限、頻率控制,但凡是數據庫、分頁等操做都須要手動去完成,比較原始。
        
    
    
       class GenericAPIView(APIView)
            
            def post(...):
                pass 
            
    b. 繼承 GenericViewSet(ViewSetMixin, generics.GenericAPIView)
        若是繼承它以後,路由中的as_view須要填寫對應關係    .as_view({'get':'list','post':'create'})
        在內部也幫助咱們提供了一些方便的方法:
            - get_queryset
            - get_object
            - get_serializer
        
        注意:要設置queryset字段,不然會跑出斷言的異常。
        # 只提供增長功能
        class TestView(GenericViewSet):
            serializer_class = XXXXXXX

            def create(self,*args,**kwargs):
                pass # 獲取數據並對數據進行操做
        
    c. 繼承 
            - ModelViewSet
            - mixins.CreateModelMixin,GenericViewSet
            - mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet
    
        對數據庫和分頁等操做不用咱們在編寫,只須要繼承相關類便可。
        
        示例:只提供增長功能
        class TestView(mixins.CreateModelMixin,GenericViewSet):
            serializer_class = XXXXXXX
    http://www.cnblogs.com/iyouyue/p/8798572.html#_label3

12.4 認證流程

- 如何編寫?寫類並實現authticate
    - 方法中能夠定義三種返回值:
        - (user,auth),認證成功
        - None , 匿名用戶
        - 異常 ,認證失敗
    - 流程:
        - dispatch 
        - 再去request中進行認證處理
    https://www.cnblogs.com/haiyan123/p/8419872.html

12.5 訪問頻率控制

- 匿名用戶,根據用戶IP或代理IP做爲標識進行記錄,爲每個用戶在redis中建立一個列表
        {
            throttle_1.1.1.1:[1526868876.497521,152686885.497521...]
            throttle_1.1.1.2:[1526868876.497521,152686885.497521...]
            throttle_1.1.1.3:[1526868876.497521,152686885.497521...]
            throttle_1.1.1.4:[1526868876.497521,152686885.497521...]
            throttle_1.1.1.5:[1526868876.497521,152686885.497521...]
        }
        
        每一個用戶再來訪問時,須要先去記錄中剔除以及過時時間,再根據列表的長度判斷是否能夠繼續訪問。
        
        如何封IP:在防火牆中進行設置
    - 註冊用戶,根據用戶名或郵箱進行判斷。
        {
            throttle_xxxx1:[1526868876.497521,152686885.497521...]
            throttle_xxxx2:[1526868876.497521,152686885.497521...]
            throttle_xxxx3:[1526868876.497521,152686885.497521...]
            throttle_xxxx4:[1526868876.497521,152686885.497521...]
        
        }
        
        每一個用戶再來訪問時,須要先去記錄中剔除以及過時時間,再根據列表的長度判斷是否能夠繼續訪問。

            1分鐘:40次

12.6 接口的冪等性(判斷是否會形成二次傷害)

一個接口經過首先進行1次訪問,而後對該接口進行N次相同訪問的時候,對訪問對象不形成影響,那麼就認爲接口具備冪等性。
    好比:
        GET,  第一次獲取數據、第二次也是獲取結果,冪等。
        POST, 第一次新增數據,第二次也會再次新增,非冪等。
        PUT,  第一次更新數據,第二次不會再次更新,冪等。
        PATCH,第一次更新數據,第二次可能再次更新,非冪等。
        DELTE,第一次刪除數據,第二次不會再次刪除,冪等。

12.7 爲何要使用django rest framework框架?

在編寫接口時能夠不適用django rest framework框架,

    若是不使用:也能夠作,那麼就能夠django的CBV來實現,開發者編寫的代碼會更多一些。
    若是  使用:內部幫助咱們提供了不少方便的組件,咱們經過配置就能夠完成相應操做,如:
            - 序列化,能夠作用戶請求數據校驗+queryset對象的序列化稱爲json
            - 解析器,獲取用戶請求數據request.data,會自動根據content-type請求頭的不能對數據進行解析
            - 分頁,將從數據庫獲取到的數據在頁面進行分頁顯示。
            還有其餘:
                - 認證
                - 權限
                - 訪問頻率控制
                ...

12.8 其餘彙總

1)渲染器有坑 
        - 指定渲染器只用JSON
        - 視圖:
            class UserView(...):
                queryset = []  # 必寫
                ...

    2)assert 是的做用?
        
        條件成立則繼續往下,不然跑出異常,通常用於:知足某個條件以後,才能執行,不然應該跑出異常。
        
        應用場景:rest framework 
            class GenericAPIView(views.APIView):        
                queryset = None
                serializer_class = None

                # If you want to use object lookups other than pk, set 'lookup_field'.
                # For more complex lookup requirements override `get_object()`.
                lookup_field = 'pk'
                lookup_url_kwarg = None

                # The filter backend classes to use for queryset filtering
                filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

                # The style to use for queryset pagination.
                pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

                def get_queryset(self):         
                    assert self.queryset is not None, (
                        "'%s' should either include a `queryset` attribute, "
                        "or override the `get_queryset()` method."
                        % self.__class__.__name__
                    )

                    queryset = self.queryset
                    if isinstance(queryset, QuerySet):
                        # Ensure queryset is re-evaluated on each request.
                        queryset = queryset.all()
                    return queryset

    3) 用django寫接口時,有沒有用什麼框架?
        - 使用rest framework框架
        - 原生CBV
        
    4) rest framework框架優勢?  
        rest framework幫助開發者提供了不少組件,能夠提升開發效率。

************************* 十三.計算機基礎*************************

13.1 HTTP協議請求方法和狀態碼

流程:
        1.域名解析
        域名解析檢查順序爲:瀏覽器自身DNS緩存---》OS自身的DNS緩存--》讀取host文件--》本地域名服務器--》權限域名服務器--》根域名服務器。若是有且沒有過時,則結束本次域名解析。域名解析成功以後,進行後續操做
        2.tcp3次握手創建鏈接
        3.創建鏈接後,發起http請求
        4.服務器端響應http請求,瀏覽器獲得到http請求的內容
        5.瀏覽器解析html代碼,並請求html代碼中的資源
        6.瀏覽器對頁面進行渲染,展示在用戶面前

    
        請求方法有8種,分別爲:
            GET:請求獲取由 Request-URI 所標識的資源。
            POST:在 Request-URI 所標識的資源後附加新的數據。
            HEAD:請求獲取由 Request-URI 所標識的資源的響應消息報頭。
            OPTIONS:請求查詢服務器的性能,或查詢與資源相關的選項和需求。
            PUT:請求服務器存儲一個資源,並用 Request-URI做爲其標識。
            DELETE:請求服務器刪除由 Request-URI所標識的資源。
            TRACE:請求服務器回送收到的請求信息,主要用語測試或診斷。
            CONNECT:HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。

    常見的響應狀態碼有如下幾種,在各類下面分別列幾個常見的狀態碼:        
       1開頭(信息)
       2開頭(成功)
           200(OK):請求成功
           202(Accepted):已接受請求,還沒有處理
           204(No Content):請求成功,且不需返回內容
       3開頭(重定向)
           301(Moved Permanently):被請求的資源已永久移動到新位置
           301(Moved Temporarily):被請求的資源已臨時移動到新位置
       4開頭(客戶端錯誤)
           400(Bad Request):請求的語義或是參數有錯
           403(Forbidden):服務器拒絕了請求
           404(Not Found):未找到請求的資源
       5開頭(服務器錯誤)
          500(Internal Server Error):服務器遇到錯誤,沒法完成請求
          502(Bad Getway):網關錯誤,通常是服務器壓力過大致使鏈接超時       
          503(Service Unavailable):服務器宕機

    https://blog.csdn.net/zixiaomuwu/article/details/60778462
相關文章
相關標籤/搜索