[TOC]
## 第二章 起步與紅圖
本章我們初始化項目,探討與研究Flask的默認層級結構。當我們遇到層級結構不合理時,我們將模仿藍圖自己定義一個“紅圖”來擴展Flask層級體系
### 2-1 環境、開發工具與flask1.0
### 2-2 初始化項目
### 2-3 新建入口文件
當我們拿到一個全新的 `flask`項目的時候,我們如何開始呢?第一件事情就是給項目建立入口文件。文件名一般與項目同名,比如當前項目名為 `ginger`,入口文件名為 `ginger.py`。
入口文件創建完成之后,首先要考慮的是如何拿到 `flask`核心對象。正如之前課程講到的,不推薦在入口文件中實例化 `flask`核心對象,推薦在項目根目錄下新建 `app`包(就是個含有`__init__`文件的目錄,目錄名字叫做 `app`),然后在 `app`包下新建`app.py`文件,我們把與 `flask`核心對象初始化相關的工作都放到`app.py`中。
### 2-4 藍圖分離視圖函數的缺陷
### 2-5 打開思維,創建自己的Redprint——紅圖

### 2-6 實現 Redprint
> 疑問:
>
> `Redpoint`的 `register`方法其實什么都沒有干,僅僅是傳入參數進來了,這樣也行?為什么呢?
>
> * `if`語句是為了加 `url`前綴
>
> * `for`語句是為了注冊路由
>
~~~
?class RedPrint:
? ? ?def __init__(self, name):
? ? ? ? ?self.name = name
? ? ? ? ?self.mound = []
??
? ? ?def route(self, rule, **options):
? ? ? ? ?def decorator(f):
? ? ? ? ? ? ?self.mound.append((f, rule, options))
? ? ? ? ? ? ?return f
? ? ? ? ?return decorator
??
? ? ?def register(self, bp, url_prefix=None):
? ? ? ? ?if url_prefix is None:
? ? ? ? ? ? ?url_prefix = '/' + self.name
? ? ? ? ?for f, rule, options in self.mound:
? ? ? ? ? ? ?endpoint = options.pop("endpoint", f.__name__)
? ? ? ? ? ? ?bp.add_url_rule(url_prefix + rule, endpoint, f, **options)
~~~
#### 知識點1
`options` 到底是什么類型的數據呢?它是一個字典;
字典的 `pop`方法是什么意思呢?是取到某一個值,并且將原來字典里的值刪除掉。
這里用法精妙的地方就在于`pop`后面還有`f.__name__`,這個意思是取默認值。因為 `options`里并不一定有以 `endpoint`為鍵的值。這句話的意思就是,如果 `options`里有以 `endpoint`為鍵的值,那么就取這個值;如果 `options`里沒有以`endpoint`這個鍵,就取`f.__name__`(視圖函數的名字)作為 `endpoint`的名字。這是非常精妙的,這就是一種非常 pythonic 的寫法!
我們可以在視圖函數里傳入 `endpoint`:
~~~
?@api.route('/', methods=['GET'], endpoint=xxx)
?def get_book():
? ? ?return 'get book'
~~~
如果你在這里傳入 `endpoint`的話,那么 `options`里面就有 `endpoint`這個鍵,就能取到值。
大多數情況下,我們都不會傳 `endpoint`,所以說大多數情況下 `endpoint` 就是視圖函數的名字。
#### 知識點2
前綴 `url_prefix`
`add_url_rule`第一個參數是**路由規則**,所以此處第一個參數應該為 `url_prefix + rule`
### 2-7 優化 Redpoint
我們在 app.api.v1.**init**.py 文件中注冊 Redpoint 的時候每次都需要寫 `url_prefix`有點煩,我們可不可以不傳 `url_prefix`呢?
~~~
?def create_blueprint_v1():
? ? ?bp_v1 = Blueprint('v1', __name__)
? ? ?
? ? ?user.api.register(bp_v1, url_prefix='/user')
? ? ?book.api.register(bp_v1, url_prefix='/book')
? ? ?client.api.register(bp_v1, url_prefix='/client')
? ? ?return bp_v1
~~~
答案是可以的。因為傳入的 `url_prefix`與`Redpoint`名稱是相同的,我們完全可以使用 `Redpoint`的名稱來代替 `url_prefix`。在 `Redpoint.register`函數中添加如下判斷就行了:
~~~
?if url_prefix is None:
? ? ?url_prefix = '/' + self.name
~~~
表明,如果不傳 `url_prefix`的話,則默認使用`/+self.name`來表示`url_prefix`。