[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——红图
![](https://ws4.sinaimg.cn/large/006tKfTcgy1g0r544s92gj31u10u04qp.jpg)
### 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`。