rainbond-python


Namerainbond-python JSON
Version 1.3.6 PyPI version JSON
download
home_pagehttps://github.com/hekaiyou/rainbond-python
SummaryRainbond python cloud native development base library
upload_time2022-12-07 08:30:31
maintainer
docs_urlNone
authorKaiyou He
requires_python
license
keywords rainbond python cloud native
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Rainbond Python

一个完整的 Python 云原生应用开发解决方案,基于以下内容:

- [Flask](https://dormousehole.readthedocs.io/en/latest/):轻量级 Python Web 应用程序框架
- [Rainbond](https://www.rainbond.com/):开源的企业级云原生平台,撑企业应用开发、架构、交付和运维的全流程

## 安装

```shell script
$ pip install rainbond-python
或者
$ pip3 install rainbond-python
```

## 快速开始

通过 `rainbond-python -c <组件名称>` 命令可以创建一个 Python 云原生组件:

```python
rainbond-python -c demo_component
```

该命令会在当前目录下创建一个组件项目,并打印如下提示:

```shell script
Successfully created `demo_component` component. Run command:
$ cd demo_component
$ pip3 install -r requirements.txt
$ python3 app.py
```

按照上面的提示,可以把一个最简单的 Python 云原生组件运行起来!

### 部署组件

将上面快速生成的组件项目上传到 Git 仓库中,在 Rainbond 应用中选择 *从源代码开始* 添加组件,通过组件的的源码地址构建云原生组件。

![部署组件01](/static_image/部署组件01.png)

构建完成后,通过添加 "http访问策略" 来配置云原生组件的访问路由。

![部署组件02](/static_image/部署组件02.png)

接下来通过 PostMan 等接口调试工具,访问 *http://xxx.com/api/v1/demo?key=value* (xxx替换成具体域名),即可验证部署是否成功。([源码地址](https://github.com/hekaiyou/demo_component/tree/v1))

### 部署存储组件

在 Rainbond 应用中选择 *从源镜像开始* 构建组件,通过组件的的镜像地址构建存储 (数据库) 组件,在当前解决方案中,使用的是 **MongoDB** 数据库,因此将 `mongo` 作为镜像地址。

![接入数据库01](/static_image/接入数据库01.png)

存储 (数据库) 组件构建完成后,需要开通 *对内服务* 并同时将别名改成 **MONGODB**,这样存储组件才能被其他组件的容器依赖和访问。

![接入数据库02](/static_image/接入数据库02.png)

通过上面的步骤,存储组件会自动创建两个依赖环境变量。

![接入数据库03](/static_image/接入数据库03.png)

在开发调试时,我们需要在本地设置这个 MongoDB 的环境变量,以 Linux 为例。

```shell script
$ set MONGODB_HOST=127.0.0.1
$ set MONGODB_PORT=27017
```

如果是 Windows 环境,打开 "高级系统设置" 里的环境变量,直接可视化编辑即可。

如果 MongoDB 设置了账号密码, 还需要额外设置下面两个环境变量:

```shell script
$ set MONGODB_NAME=user1
$ set MONGODB_PASSWORD=123456
```

1. 先给容器添加 `--auth` 启动参数命令, 再进入容器内部, 执行以下代码添加管理员账号密码.
    ```shell script
    # 进入 admin
    $ mongo admin 
    # 创建用户和密码
    $ db.createUser({ user: 'admin', pwd: '123456', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });
    # 验证一下对不对
    $ db.auth("admin","123456");
    # 退出
    $ exit
    # 修改密码(补充)
    $ use admin;
    $ db.changeUserPassword('admin','88889999')
    ```
2. 设置一个普通用户.
    ```shell script
    # 进入 admin
    $ mongo admin 
    # 在 admin 数据库认证成功
    $ db.auth('admin','123456');
    # 切换到 universal 数据库
    $ use universal;
    # 不会在提示没有权限了
    $ show collections;
    # 为 universal 数据库添加了一个管理用户 user1
    $ db.createUser({ user: 'user1', pwd: '123456', roles: [ { role: "readWrite", db: "universal" } ] });
    # 为 universal 数据库添加了一个只读用户 user2
    $ db.createUser({ user: 'user2', pwd: '123456', roles: [ { role: "read", db: "universal" } ] });
    # 退出
    $ exit
    # 修改权限(补充)
    $ db.updateUser('user1', { pwd: '123456', roles: [ { role: "readWrite", db: "universal" } ] });
    ```

### 数据库读写

作为快速开始的部分,这里只展示最简单的增删改查操作。([源码地址](https://github.com/hekaiyou/demo_component/tree/v2))首先,我们接着上面的组件项目,在开头添加两行代码。

```python
from flask import Flask, request
from rainbond_python.parameter import Parameter
from rainbond_python.error_handler import error_handler
from rainbond_python.db_connect import DBConnect  # 添加的代码

app = Flask(__name__)
error_handler(app)

db_books = DBConnect(db='demo', collection='books')  # 添加的代码
```

上面的 `DBConnect` 类是处理 MongoDB 读写行为的通用类,通过在初始化时,指定 `db` 和 `collection` 参数,我们可以连接到 MongoDB 的指定库、指定集合中。

#### 添加数据

修改组件项目的 **POST** 部分代码。

```python
    elif parameter.method == 'POST':
        param = parameter.verification(
            checking=parameter.param_json,
            verify={'title': str, 'author': str}
        )
        new_id = db_books.write_one_docu(docu=param)
        return {'new_id': new_id}
```

现在,我们往 `books` 集合中添加一本图书文档。

![数据库读写01](/static_image/数据库读写01.png)

上面 `Parameter` 类的 `verification()` 是一个非常实用的方法,它可以判断请求内容是否满足需求,我们可以放心使用通过 `verification()` 方法检查后的数据。而 `DBConnect` 类的 `write_one_docu()` 方法则用于写入单条文档,它接收一个字典,而恰好 `verification()` 方法返回的就是一个字典,所以可以直接作为数据写入。

#### 查询数据

修改组件项目的 **GET** 部分代码。

```python
    if parameter.method == 'GET':
        find_data = db_books.find_paging(parameter)
        return find_data
```

现在,我们查询 `books` 集合中的所有图书文档。

![数据库读写02](/static_image/数据库读写02.png)

上面 `DBConnect` 类的 `find_paging()` 同样是一个特别实用的方法,如上面所见,你只需要把请求内容交给它,它会直接返回查询结果给你。

你还可以试试多写入几个文档,然后请求 `http://127.0.0.1:5000/api/v1/demo?author=李杰` 接口,看看会有什么神奇的效果?

#### 更新数据

修改组件项目的 **PUT** 部分代码。

```python
    elif parameter.method == 'PUT':
        param = parameter.verification(
            checking=parameter.param_json,
            verify={'id': str, 'title': str, 'author': str}
        )
        update_result = db_books.update_docu(
            find_docu={'id': param['id']},
            modify_docu={'title': param['title'], 'author': param['author']}
        )
        return update_result
```

现在,我们更新指定 `id` 的 `books` 集合中的图书文档。

![数据库读写03](/static_image/数据库读写03.png)

上面 `DBConnect` 类的 `update_docu()` 方法用于更新文档,它会返回更新结果。

#### 删除数据

修改组件项目的 **DELETE** 部分代码。

```python
    elif parameter.method == 'DELETE':
        param = parameter.verification(
            checking=parameter.param_json,
            verify={'id': str}
        )
        delete_result = db_books.delete_docu(find_docu=param)
        return delete_result
```

现在,我们删除指定 `id` 的 `books` 集合中的图书文档。

![数据库读写04](/static_image/数据库读写04.png)

上面 `DBConnect` 类的 `delete_docu()` 方法用于删除文档,它会返回删除结果。

### 建立组件依赖

完成一个简单的图书管理组件后,我们要将组件部署到 Rainbond 平台,在 Rainbond 应用中选择 *切换到编辑模式* 进入依赖关系编辑页面,将 *计算组件* 依赖到 *存储组件* 即 **Mongo DB** 组件。

![建立组件依赖01](/static_image/建立组件依赖01.png)

重新构建 *计算组件* 即 **Demo Component** 组件,完成构建并滚动更新后,通过日志可以看到组件服务已经在正常运行了。

![建立组件依赖02](/static_image/建立组件依赖02.png)

需要注意:在 Rainbond 平台更改组件依赖关系后,相关的组件需要进行滚动更新。

## 进阶

### 异常处理

通过 `error_handler()` 方法,可以将常用的 *4xx* 和 *5xx* 状态码的异常响应委托给 **rainbond-python** 处理,它会自动捕获这些异常并返回响应。

```python
......
from flask import Flask, request
from rainbond_python.error_handler import error_handler
......
app = Flask(__name__)
error_handler(app)
......
```

#### 跨域处理

默认情况下 `error_handler()` 方法通过 `flask_cors` 库一键处理了服务端跨域问题,如果需要考虑安全问题,可以通过 `error_handler(app, simple_cors=False)` 取消跨域支持,同时,还可以重写跨域逻辑。

```python
......
error_handler(app, simple_cors=False)
from flask_cors import CORS
CORS(app, resources={r'/.*': {'origins': 'http://127.0.0.1:8888'}})
......
```

#### 业务异常

使用 `handle_abnormal()` 方法可以主动抛出业务逻辑异常,简单示例如下。

```python
......
from rainbond_python.tools import handle_abnormal
......
@app.route('/api/v1/demo', methods=['GET'])
def api_demo():
    parameter = Parameter(request)

    if parameter.method == 'GET':
        handle_abnormal(message='异常信息~~~', status=400)
......
```

这样,当用户请求时,响应内容如下:

```json
{
    "code": 400,
    "message": "异常信息~~~",
    "server_time": 20210220143830000,
    "host_name": "Z0jli2o0d2ymott0ggs6m",
    "host_ip": "128.19.80.115"
}
```

还可以通过 `header` 参数字典设置响应头字典,通过 `other` 参数添加附加信息字典。

```python
handle_abnormal(message='2333~~~', status=400, other={'key1': 'value1', 'key2': {'a': 1}})
```

这样就可以将更详细的提示信息告知用户,响应内容如下:

```json
{
    "code": 400,
    "message": "2333~~~",
    "server_time": 20210220144625000,
    "host_name": "Z0jli2o0d2ymott0ggs6m",
    "host_ip": "128.19.80.115",
    "key1": "value1",
    "key2": {
        "a": 1
    }
}
```

当然,正常的业务逻辑也可以使用 `handle_abnormal()` 方法返回,但是考虑到业务逻辑的不确定性和复杂性,一般将作为异常逻辑的响应使用。

## 数据备份

mongo数据库的备份,是通过在rainbond平台mongo组件中安装插件的方式实现的,并且配合开源云存储服务,将备份数据上传至云端。

#### 插件安装步骤如下:

1.  ![数据库备份插件01](static_image/数据库备份插件01.png)
3.  ![数据库备份插件02](static_image/数据库备份插件02.png)
4.  ![数据库备份插件03](static_image/数据库备份插件03.png)
5.  ![数据库备份插件04](static_image/数据库备份插件04.png)
6.  ![数据库备份插件05](static_image/数据库备份插件05.png)

###### 插件参数说明
* 必须参数:

MONGODB_HOST : 数据库地址

MONGODB_PORT : 数据库端口

MONGODB_DB : 需要备份的数据库名称

FILE_DIR : 备份数据本地保存相对路径(同时也是上传备份数据的接口参数之一)

URL: 上传数据备份接口地址

* 定时任务参数(只支持以下参数)

DAY : 天循环

DAY_OF_WEEK: 周期循环

HOUR:小时循环

MINUTE:分钟循环

SECOND:秒循环

相似用法可参考 https://www.jianshu.com/p/4c5bc85fc3fd中2.1.3章节,第三点 crontab触发器用法

#### 开源云存储服务安装(可使用rainbond直接安装即可使用)
参考链接:https://www.laobuluo.com/2934.html

### Parameter

处理请求与响应参数的通用类。

```python
from rainbond_python.parameter import Parameter
```

#### 获取请求参数

通过 `Parameter` 类实例,可以获取以下信息:

- parameter.method: 请求类型
- parameter.headers: 请求头
- parameter.param_url: URL中传递的参数
- parameter.param_json: Json请求中的参数
- parameter.param_form: 表单请求中的参数

所有信息均为字典类型,通过 `json.dumps()` 可以直接作为响应返回:

```python
@app.route('/api/1.0/demo', methods=['GET', 'POST', 'PUT', 'DELETE'])
def api_demo():
    parameter = Parameter(request)
    if parameter.method == 'GET':
        return json.dumps(parameter.param_url, ensure_ascii=False), 200, []
    elif parameter.method == 'POST':
        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []
    elif parameter.method == 'PUT':
        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []
    elif parameter.method == 'DELETE':
        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []
```

#### 校验参数内容

通过 `Parameter` 类的 `verification()` 方法,可以判断参数字典是否符合要求:

```python
    elif parameter.method == 'POST':
        param = parameter.verification(checking=parameter.param_json, verify={'name': str, 'age': int})
```

其中 `checking` 参数是需要校验的参数字典,通常传递 `parameter.param_url`、`parameter.param_json` 或 `parameter.param_form`。第二个 `verify` 参数则是校验内容字典,需要指定 *参数名* 和 *参数类型* 作为字典项。如果请求中包含可选参数,可以将该参数的名称及其默认值输入到 `optional` 参数中,例如可以设置 *age* 参数为空时,默认填充为 *18* 岁:

```python
parameter.verification(checking=parameter.param_json, verify={'name': str, 'age': int}, optional={'age': 18})
```

如果判断失败,则直接返回异常响应,响应体中包含明确的提示信息。默认情况下,`str` 类型的 必选参数不能为空字符串,如果需要为空,可以通过 `null_value=True` 进行设置,或者将其作为可选参数处理。

#### 校验文件表单

如果需要接收表单提交的文件对象,可以使用 `verification_file()` 方法对请求中的表单文件字段进行校验:

```python
    elif parameter.method == 'POST':
        param = parameter.verification(checking=parameter.param_form, verify={'id': str})
        param_file = parameter.verification_file(verify_field=['updata'])
```

如上面的代码,如果请求中没有名为 *updata* 的表单文件字段,会直接返回异常信息。该方法与 `verification()` 方法可以同时使用。如果还需要判断上传文件的后缀名,可以通过 `verify_suffix` 参数进行配置:

```python
param_file = parameter.verification_file(verify_field=['updata'], verify_suffix=['jpg'])
# 二者效果相同,但是列表类型可以同时指定多个后缀名称
param_file = parameter.verification_file(verify_field=['updata'], verify_suffix=[['jpg']])
```

该方法会返回 `werkzeug.datastructures.ImmutableMultiDict` 对象,即通过 `request.files` 获取到的对象,接下来就可以:

- 通过 `param_file.get('xxxx')` 获取到文件对象
- 通过 `param_file.get('xxxx').filename` 获取具体文件名称
- 通过 `param_file.get('xxxx').save('/xxx/xxx.jpg')` 保存文件到本地

### DBConnect

处理 MongoDB 读写行为的通用类。

```python
from rainbond_python.db_connect import DBConnect
db = DBConnect(db='db_name', collection='collection_name')
```

#### 分页查询

支持 **GET** 和 **POST** 请求,使用非常简单,直接把 `Parameter` 类的实例传递给 `DBConnect` 类的 `find_paging()` 方法即可:

```python
@app.route('/api/1.0/demo', methods=['GET'])
def api_demo():
    parameter = Parameter(request)
    if parameter.method == 'GET':
        find_data = db.find_paging(parameter)
        return find_data, 200, []
```

内部组件或外部客户端通过 */api/v1/demo?$offset=0&$limit=15&name=sb* 即可访问,请求参数如下:

- $limit: 可选,指示页大小,从 `1` 开始计算,默认 `10` 条数据(单纯的数数,数几个就几个)
- $offset: 可选,指示记录起始位置,默认从 `0` 开始计算(代码逻辑,第几就是从数据库的第几条开始取文档)
- $orderby: 可选,排序规则(Eg: `key1 desc,key2 asc`),asc=升序、desc=降序
- $start_date: 可选,开始日期(区间查询),支持日期(2020-10-1)格式和时间戳(601481600)格式
- $end_date: 可选,结束日期(区间查询),同上,必须成对出现
- $date_type: 可选,区间查询的字段,默认为更新时间(`update_time`),可以设置成按创建时间(`creation_time`)查询
- <任意筛选字段>: 可选,任意筛选值

响应字段如下:

- total: 查询到的文档总数
- items: 文档列表
- dummy_remove: 假删除文档数(不参与查询)

由于技术原因,筛选字段目前不支持 `int` 型数据的模糊查询。同时 `$start_date` 和 `$end_date` 如果传递的是时间戳格式,能精确到秒。(更复杂的查询,可以将查询值提前处理成字典,这样可以实现一些非常规性的需求)

#### 写文档

##### 写入单个文档

```python
insert_dict = {'name': 'Xiao Ming', 'age': 23}
db.write_one_docu(docu=insert_dict)
```

如果写入失败,会直接返回异常响应,如果成功则会返回新数据的 `_id` 值。

##### 写入多个文档

```python
insert_dict_list = [{'name': 'Xiao Ming', 'age': 23},{'name': 'lao Yang', 'age': 35}]
db.write_many_docu(docu_list=insert_dict_list)
```

如果写入失败,会直接返回异常响应,如果成功则会返回新数据的 `_id`值的列表。

#### 文档是否存在

```python
examine_dict = {'name': 'Xiao Ming'}
if db.does_it_exist(docu=examine_dict):
    print('Docu already exists')
else:
    print('Docu does not exist')
```

#### 更新文档

同样的,如果更新失败,也会直接返回异常响应。

##### 更新单个匹配文档

```python
find_dict = {'name': 'Xiao Ming'}
modify_dict = {'name': 'Xiao Hong'}
db.update_docu(find_docu=find_dict, modify_docu=modify_dict)
```

##### 更新全部匹配文档

```python
find_dict = {'age': 23}
modify_dict = {'name': '23 year old'}
db.update_docu(find_docu=find_dict, modify_docu=modify_dict, many=True)
```

该方法会返回一个包含 `matched_count` 和 `modified_count` 即匹配/影响数据条数的字典。


#### 更新文档-指定字段自增

```python
find_dict = {'name': 'xiao yang'}
modify_dict = {'age': 1}
db.update_docu_inc(find_docu=find_dict, modify_docu=modify_dict)
```
表示age字段数字增加1,原先age=18,调用该方法后,age=19
该方法会返回一个包含 `matched_count` 和 `modified_count` 即匹配/影响数据条数的字典。

#### 删除文档

删除文档分为 **真删除** 和 **假删除** 两种方式,通过 `delete_docu()` 方法实现,该方法会返回一个包含 `deleted_count` 和 `false_delete` 的字典。。

##### 真删除文档

```python
db.delete_docu(find_docu={'id': '60053fa139842d28d7563c6c'})
```

##### 假删除文档

```python
db.delete_docu(find_docu={'id': '60053fa139842d28d7563c6c'}, false_delete=True)
```

假删除操作会在对应的文档中添加一个 `remove_time` 字段,里面记录这个文档被移除的时间。

##### 批量删除文档

```python
db.delete_docu(find_docu={'id': {'$in': ['111', '222']}}, many=True)
```

#### 查询文档

通过 `find_docu()` 标准查询方法时,无论查询单个还是多个,返回均是 `list` 类型数据,没有匹配数据时返回空列表。

##### 查询单个匹配文档

```python
find_dict = {'title': {'$regex': '标题'}}
find_data_list = db.find_docu(find_dict=find_dict, many=False)
print(find_data_list[0])
```

##### 查询全部匹配文档

```python
find_dict = {'title': {'$regex': '标题'}}
find_data_list = db.find_docu(find_dict=find_dict)
for find_data in find_data_list:
    print(find_data)
```

##### 根据id查找文档

```python
from rainbond_python.db_connect import DBConnect
db = DBConnect('unitest_rainbond_python', 'test_db_connect')
id = db.write_one_docu({'name': 'LaoXu'})
docu = db.find_docu_by_id(str(id))

# 当id不存在时,默认会使用abort抛出异常
fail_docu = db.find_docu_by_id('6008daa19223551b00548ded')
# 可以将raise_err=False时,id不存在会返回None
fail_docu = db.find_docu_by_id('6008daa19223551b00548ded',raise_err=False)
```

该方法返回记录字典,且把'_id'转换为了str类型

##### 根据id列表查找文档

```python
from rainbond_python.db_connect import DBConnect
db = DBConnect('unitest_rainbond_python', 'test_db_connect')
docu_list = db.find_docu_by_id_list(['6008daa19223551b00548ded','6008daa29223551b00548dee'])
```

该方法返回记录字典列表,且把'_id'转换为了str类型。当所有id不存在时,返回[]

#### 根据字段去重查询文档

```python
num = find_docu_distinct('age')
``````
返回int类型数字,代表文档中age字段去重后的数量

### 文件下载

在网络上传输文件,目前主要有下载和流式传输两种方案,分别 `rainbond_python.download` 包的对应 `download_file()` 和 `download_flow()` 方法。

#### 普通下载

通常用于文档文件(压缩包/PDF/TXT等文档),这种方式必须等全部内容传输完毕后,才能在本地机器打开:

```python
......
from rainbond_python.download import download_file
......
    if parameter.method == 'GET':
        download_response = download_file(file_path='C:/Users/xxx/Desktop', file_name='新建文本文档.txt'])
        return download_response
......
```

#### 流式传输

通常用于多媒体文件(视频/音频/直播流等场景),文件信息由服务器向用户计算机连续实时地传送,不必等到整个文件全部下载完毕,通常经过几秒或十几秒的启动延时即可打开:

```python
......
from rainbond_python.download import download_flow
......
    if parameter.method == 'GET':
        download_response = download_flow(file_path='C:/Users/xxx/Desktop', file_name='微视频.mp4'])
        return download_response
......
```

#### 打包目录生成zip文件下载(在内存中打包)

通常用于将现有目录文件,打包成zip文件,提供用户下载。打包zip文件数据在内存中完成,完成后从内存中读取二进制数据,并且回收内存。
参数save_zip表示本地是否存储打包zip文件,默认值为 False,为True时,打包文件保存在打包目录同级

```python
from rainbond_python.download import download_directory

......
    if parameter.method == 'GET':
        download_response = download_directory(dir_path='C:/Users/xxx/project', zip_name='project.zip'],save_zip=False)
        return download_response
......
```

### RedisConnect

处理 Redis 读写行为的通用类。

```python
from rainbond_python.redis_connect import RedisConnect
redis_connect = RedisConnect(db=0)
```

### 权限认证

认证中心组件之前,需要将组件依赖于 **认证中心**、**Redis** 和 **MongoDB** 组件,然后在业务代码中编写如下代码,完成接入:

```python
......
from flask import Flask, request, session
from rainbond_python.verify_token import VerifyToken, set_token_session
......
# 注册权限信息到认证中心组件
per_defaults = [
    {'session_key': 'auth_xxxx', 'center_name': '组件名称', 'permission_name': '自定义权限', 'status': [0, 1, 2]},
]
token = VerifyToken(per_defaults=per_defaults)
set_token_session(app, verify=token, per_defaults=per_defaults)
......
@app.route('/api/v1/demo', methods=['GET'])
def api_v1_demo():
    # 获取用户权限:0-1-2-4-8-16-32-64 (无权限-查看-新增-编辑-删除-下载-……)
    # Eg: [0, 1, 2]
    permission_list = session['auth_reports']['permission_list']

    if parameter.method == 'GET' and (1 in permission_list):  # 用户有查询权限
        # 通过认证并符合权限后,可以执行业务逻辑
        psss
......
```

注册权限信息到认证中心组件的时候,`center_name` 和 `permission_name` 建议都以中文命名,因为前端管理页面会直接读取这两个字段。`session_key` 是写到会话中,建议使用英文命名。然后根据上面的注册内容,我们可以从会话中获取用户认证信息:

- session['auth_xxxx']['permission_list']: 0-1-2-4-8-16-32-64 (无权限-查看-新增-编辑-删除-下载-……)
- session['auth_xxxx']['is_all_data']: 前用户是否能查看全部数据
- session['real_name']: 账户真实姓名
- session['user_name']: 账户名称
- session['token_id']: 账户ID

如果需要指定某些路由不参与认证,可以通过 `set_token_session` 方法的 `whitelist` 参数设置白名单,例如 `whitelist=['/api/v1/demo']` 则表示该路由可以随意被请求。

### 通用方法

#### handle_date()

将 *2020-10-1* 或 *601481600* 即日期格式或时间戳格式的字符串,处理成 Python 的 `datetime.datetime` 数据:

```python
from rainbond_python.tools import handle_date
print(handle_date(date='2020-10-1'))
print(handle_date(date='2020-10-31', date_type='end'))
```

通过 `date_type` 可以设置是日期的开始(`start`)还是一天的结束(`end`)时间。

#### handle_db_dict()

将 MongoDB 字典数据中的 `_id` 转换为 `str` 类型、时间转换成时间戳:

```python
query_dict = self.mongo_collection.find_one({'title': {'$regex': '标题1'}})
handle_db_dict(query_dict)
```

#### handle_db_to_list()

将 MongoDB 的列表中的 `_id` 转换为 `str` 类型,并转换为字典列表(原db的id是ObjectId类型,转为json会报错):

```python
from rainbond_python.tools import handle_db_to_list
from rainbond_python.db_connect import DBConnect

def test_handle_db_to_list():
    db = DBConnect('unitest_rainbond_python', 'test_parameter')
    old_list = db.mongo_collection.find({})
    new_list = handle_db_to_list(old_list)
    print('new_list is a list of dict',new_list)
```

#### handle_time_difference()

计算前端传递的两个时间戳之间,相差多少秒,返回 `float` 类型,解决前后端时间戳(前端的毫秒是整数位、Python的毫秒是小数位)差异问题:

```python
from rainbond_python.tools import handle_time_difference
handle_time_difference(start_timestamp=1614051008, end_timestamp=1614051008)
```

## 开发与测试

### 调试开发

基础调试代码的 `demo.py` 即 *rainbond -c demo-component* 命令创建项目中的 `app.py` 文件,是一个可以快速开始的基础代码项目。

在本地调试时,在 **demos** 目录下创建 `dev_xxxxx.py`,并复制 `demo.py` 文件里的代码,并在里面调试 *rainbond_python* 目录下的代码。(本地创建的 dev_*.py 文件会被忽略,不会被提交),同时要在开头处添加下面代码,以调用基础包中的代码:

```python
......
import sys
sys.path.append('..')
from rainbond_python.parameter import Parameter
from rainbond_python.error_handler import error_handler
from rainbond_python.db_connect import DBConnect
......
```

### 单元测试

单元测试在 /tests/* 目录下

* 执行单元测试
```shell script
$ pytest
```

## 参考

- [Restful API](https://www.runoob.com/w3cnote/restful-architecture.html) : 具体的组件API开发标准
- [12 Factor](https://12factor.net/zh_cn/) : 符合十二要素的才是云原生应用
- [RainBond](https://www.rainbond.com/docs/) : 一个开源的云原生平台
            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/hekaiyou/rainbond-python",
    "name": "rainbond-python",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "rainbond python cloud native",
    "author": "Kaiyou He",
    "author_email": "hky0313@outlook.com",
    "download_url": "https://files.pythonhosted.org/packages/a5/a5/fa96323e60ac6fd57b7e6783fd6161076dfe25589fe02bf10b420ec1e30f/rainbond-python-1.3.6.tar.gz",
    "platform": null,
    "description": "# Rainbond Python\n\n\u4e00\u4e2a\u5b8c\u6574\u7684 Python \u4e91\u539f\u751f\u5e94\u7528\u5f00\u53d1\u89e3\u51b3\u65b9\u6848\uff0c\u57fa\u4e8e\u4ee5\u4e0b\u5185\u5bb9\uff1a\n\n- [Flask](https://dormousehole.readthedocs.io/en/latest/)\uff1a\u8f7b\u91cf\u7ea7 Python Web \u5e94\u7528\u7a0b\u5e8f\u6846\u67b6\n- [Rainbond](https://www.rainbond.com/)\uff1a\u5f00\u6e90\u7684\u4f01\u4e1a\u7ea7\u4e91\u539f\u751f\u5e73\u53f0\uff0c\u6491\u4f01\u4e1a\u5e94\u7528\u5f00\u53d1\u3001\u67b6\u6784\u3001\u4ea4\u4ed8\u548c\u8fd0\u7ef4\u7684\u5168\u6d41\u7a0b\n\n## \u5b89\u88c5\n\n```shell script\n$ pip install rainbond-python\n\u6216\u8005\n$ pip3 install rainbond-python\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n\u901a\u8fc7 `rainbond-python -c <\u7ec4\u4ef6\u540d\u79f0>` \u547d\u4ee4\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a Python \u4e91\u539f\u751f\u7ec4\u4ef6\uff1a\n\n```python\nrainbond-python -c demo_component\n```\n\n\u8be5\u547d\u4ee4\u4f1a\u5728\u5f53\u524d\u76ee\u5f55\u4e0b\u521b\u5efa\u4e00\u4e2a\u7ec4\u4ef6\u9879\u76ee\uff0c\u5e76\u6253\u5370\u5982\u4e0b\u63d0\u793a\uff1a\n\n```shell script\nSuccessfully created `demo_component` component. Run command:\n$ cd demo_component\n$ pip3 install -r requirements.txt\n$ python3 app.py\n```\n\n\u6309\u7167\u4e0a\u9762\u7684\u63d0\u793a\uff0c\u53ef\u4ee5\u628a\u4e00\u4e2a\u6700\u7b80\u5355\u7684 Python \u4e91\u539f\u751f\u7ec4\u4ef6\u8fd0\u884c\u8d77\u6765\uff01\n\n### \u90e8\u7f72\u7ec4\u4ef6\n\n\u5c06\u4e0a\u9762\u5feb\u901f\u751f\u6210\u7684\u7ec4\u4ef6\u9879\u76ee\u4e0a\u4f20\u5230 Git \u4ed3\u5e93\u4e2d\uff0c\u5728 Rainbond \u5e94\u7528\u4e2d\u9009\u62e9 *\u4ece\u6e90\u4ee3\u7801\u5f00\u59cb* \u6dfb\u52a0\u7ec4\u4ef6\uff0c\u901a\u8fc7\u7ec4\u4ef6\u7684\u7684\u6e90\u7801\u5730\u5740\u6784\u5efa\u4e91\u539f\u751f\u7ec4\u4ef6\u3002\n\n![\u90e8\u7f72\u7ec4\u4ef601](/static_image/\u90e8\u7f72\u7ec4\u4ef601.png)\n\n\u6784\u5efa\u5b8c\u6210\u540e\uff0c\u901a\u8fc7\u6dfb\u52a0 \"http\u8bbf\u95ee\u7b56\u7565\" \u6765\u914d\u7f6e\u4e91\u539f\u751f\u7ec4\u4ef6\u7684\u8bbf\u95ee\u8def\u7531\u3002\n\n![\u90e8\u7f72\u7ec4\u4ef602](/static_image/\u90e8\u7f72\u7ec4\u4ef602.png)\n\n\u63a5\u4e0b\u6765\u901a\u8fc7 PostMan \u7b49\u63a5\u53e3\u8c03\u8bd5\u5de5\u5177\uff0c\u8bbf\u95ee *http://xxx.com/api/v1/demo?key=value* (xxx\u66ff\u6362\u6210\u5177\u4f53\u57df\u540d)\uff0c\u5373\u53ef\u9a8c\u8bc1\u90e8\u7f72\u662f\u5426\u6210\u529f\u3002\uff08[\u6e90\u7801\u5730\u5740](https://github.com/hekaiyou/demo_component/tree/v1)\uff09\n\n### \u90e8\u7f72\u5b58\u50a8\u7ec4\u4ef6\n\n\u5728 Rainbond \u5e94\u7528\u4e2d\u9009\u62e9 *\u4ece\u6e90\u955c\u50cf\u5f00\u59cb* \u6784\u5efa\u7ec4\u4ef6\uff0c\u901a\u8fc7\u7ec4\u4ef6\u7684\u7684\u955c\u50cf\u5730\u5740\u6784\u5efa\u5b58\u50a8 (\u6570\u636e\u5e93) \u7ec4\u4ef6\uff0c\u5728\u5f53\u524d\u89e3\u51b3\u65b9\u6848\u4e2d\uff0c\u4f7f\u7528\u7684\u662f **MongoDB** \u6570\u636e\u5e93\uff0c\u56e0\u6b64\u5c06 `mongo` \u4f5c\u4e3a\u955c\u50cf\u5730\u5740\u3002\n\n![\u63a5\u5165\u6570\u636e\u5e9301](/static_image/\u63a5\u5165\u6570\u636e\u5e9301.png)\n\n\u5b58\u50a8 (\u6570\u636e\u5e93) \u7ec4\u4ef6\u6784\u5efa\u5b8c\u6210\u540e\uff0c\u9700\u8981\u5f00\u901a *\u5bf9\u5185\u670d\u52a1* \u5e76\u540c\u65f6\u5c06\u522b\u540d\u6539\u6210 **MONGODB**\uff0c\u8fd9\u6837\u5b58\u50a8\u7ec4\u4ef6\u624d\u80fd\u88ab\u5176\u4ed6\u7ec4\u4ef6\u7684\u5bb9\u5668\u4f9d\u8d56\u548c\u8bbf\u95ee\u3002\n\n![\u63a5\u5165\u6570\u636e\u5e9302](/static_image/\u63a5\u5165\u6570\u636e\u5e9302.png)\n\n\u901a\u8fc7\u4e0a\u9762\u7684\u6b65\u9aa4\uff0c\u5b58\u50a8\u7ec4\u4ef6\u4f1a\u81ea\u52a8\u521b\u5efa\u4e24\u4e2a\u4f9d\u8d56\u73af\u5883\u53d8\u91cf\u3002\n\n![\u63a5\u5165\u6570\u636e\u5e9303](/static_image/\u63a5\u5165\u6570\u636e\u5e9303.png)\n\n\u5728\u5f00\u53d1\u8c03\u8bd5\u65f6\uff0c\u6211\u4eec\u9700\u8981\u5728\u672c\u5730\u8bbe\u7f6e\u8fd9\u4e2a MongoDB \u7684\u73af\u5883\u53d8\u91cf\uff0c\u4ee5 Linux \u4e3a\u4f8b\u3002\n\n```shell script\n$ set MONGODB_HOST=127.0.0.1\n$ set MONGODB_PORT=27017\n```\n\n\u5982\u679c\u662f Windows \u73af\u5883\uff0c\u6253\u5f00 \"\u9ad8\u7ea7\u7cfb\u7edf\u8bbe\u7f6e\" \u91cc\u7684\u73af\u5883\u53d8\u91cf\uff0c\u76f4\u63a5\u53ef\u89c6\u5316\u7f16\u8f91\u5373\u53ef\u3002\n\n\u5982\u679c MongoDB \u8bbe\u7f6e\u4e86\u8d26\u53f7\u5bc6\u7801, \u8fd8\u9700\u8981\u989d\u5916\u8bbe\u7f6e\u4e0b\u9762\u4e24\u4e2a\u73af\u5883\u53d8\u91cf:\n\n```shell script\n$ set MONGODB_NAME=user1\n$ set MONGODB_PASSWORD=123456\n```\n\n1. \u5148\u7ed9\u5bb9\u5668\u6dfb\u52a0 `--auth` \u542f\u52a8\u53c2\u6570\u547d\u4ee4, \u518d\u8fdb\u5165\u5bb9\u5668\u5185\u90e8, \u6267\u884c\u4ee5\u4e0b\u4ee3\u7801\u6dfb\u52a0\u7ba1\u7406\u5458\u8d26\u53f7\u5bc6\u7801.\n    ```shell script\n    # \u8fdb\u5165 admin\n    $ mongo admin \n    # \u521b\u5efa\u7528\u6237\u548c\u5bc6\u7801\n    $ db.createUser({ user: 'admin', pwd: '123456', roles: [ { role: \"userAdminAnyDatabase\", db: \"admin\" } ] });\n    # \u9a8c\u8bc1\u4e00\u4e0b\u5bf9\u4e0d\u5bf9\n    $ db.auth(\"admin\",\"123456\");\n    # \u9000\u51fa\n    $ exit\n    # \u4fee\u6539\u5bc6\u7801(\u8865\u5145)\n    $ use admin;\n    $ db.changeUserPassword('admin','88889999')\n    ```\n2. \u8bbe\u7f6e\u4e00\u4e2a\u666e\u901a\u7528\u6237.\n    ```shell script\n    # \u8fdb\u5165 admin\n    $ mongo admin \n    # \u5728 admin \u6570\u636e\u5e93\u8ba4\u8bc1\u6210\u529f\n    $ db.auth('admin','123456');\n    # \u5207\u6362\u5230 universal \u6570\u636e\u5e93\n    $ use universal;\n    # \u4e0d\u4f1a\u5728\u63d0\u793a\u6ca1\u6709\u6743\u9650\u4e86\n    $ show collections;\n    # \u4e3a universal \u6570\u636e\u5e93\u6dfb\u52a0\u4e86\u4e00\u4e2a\u7ba1\u7406\u7528\u6237 user1\n    $ db.createUser({ user: 'user1', pwd: '123456', roles: [ { role: \"readWrite\", db: \"universal\" } ] });\n    # \u4e3a universal \u6570\u636e\u5e93\u6dfb\u52a0\u4e86\u4e00\u4e2a\u53ea\u8bfb\u7528\u6237 user2\n    $ db.createUser({ user: 'user2', pwd: '123456', roles: [ { role: \"read\", db: \"universal\" } ] });\n    # \u9000\u51fa\n    $ exit\n    # \u4fee\u6539\u6743\u9650(\u8865\u5145)\n    $ db.updateUser('user1', { pwd: '123456', roles: [ { role: \"readWrite\", db: \"universal\" } ] });\n    ```\n\n### \u6570\u636e\u5e93\u8bfb\u5199\n\n\u4f5c\u4e3a\u5feb\u901f\u5f00\u59cb\u7684\u90e8\u5206\uff0c\u8fd9\u91cc\u53ea\u5c55\u793a\u6700\u7b80\u5355\u7684\u589e\u5220\u6539\u67e5\u64cd\u4f5c\u3002\uff08[\u6e90\u7801\u5730\u5740](https://github.com/hekaiyou/demo_component/tree/v2)\uff09\u9996\u5148\uff0c\u6211\u4eec\u63a5\u7740\u4e0a\u9762\u7684\u7ec4\u4ef6\u9879\u76ee\uff0c\u5728\u5f00\u5934\u6dfb\u52a0\u4e24\u884c\u4ee3\u7801\u3002\n\n```python\nfrom flask import Flask, request\nfrom rainbond_python.parameter import Parameter\nfrom rainbond_python.error_handler import error_handler\nfrom rainbond_python.db_connect import DBConnect  # \u6dfb\u52a0\u7684\u4ee3\u7801\n\napp = Flask(__name__)\nerror_handler(app)\n\ndb_books = DBConnect(db='demo', collection='books')  # \u6dfb\u52a0\u7684\u4ee3\u7801\n```\n\n\u4e0a\u9762\u7684 `DBConnect` \u7c7b\u662f\u5904\u7406 MongoDB \u8bfb\u5199\u884c\u4e3a\u7684\u901a\u7528\u7c7b\uff0c\u901a\u8fc7\u5728\u521d\u59cb\u5316\u65f6\uff0c\u6307\u5b9a `db` \u548c `collection` \u53c2\u6570\uff0c\u6211\u4eec\u53ef\u4ee5\u8fde\u63a5\u5230 MongoDB \u7684\u6307\u5b9a\u5e93\u3001\u6307\u5b9a\u96c6\u5408\u4e2d\u3002\n\n#### \u6dfb\u52a0\u6570\u636e\n\n\u4fee\u6539\u7ec4\u4ef6\u9879\u76ee\u7684 **POST** \u90e8\u5206\u4ee3\u7801\u3002\n\n```python\n    elif parameter.method == 'POST':\n        param = parameter.verification(\n            checking=parameter.param_json,\n            verify={'title': str, 'author': str}\n        )\n        new_id = db_books.write_one_docu(docu=param)\n        return {'new_id': new_id}\n```\n\n\u73b0\u5728\uff0c\u6211\u4eec\u5f80 `books` \u96c6\u5408\u4e2d\u6dfb\u52a0\u4e00\u672c\u56fe\u4e66\u6587\u6863\u3002\n\n![\u6570\u636e\u5e93\u8bfb\u519901](/static_image/\u6570\u636e\u5e93\u8bfb\u519901.png)\n\n\u4e0a\u9762 `Parameter` \u7c7b\u7684 `verification()` \u662f\u4e00\u4e2a\u975e\u5e38\u5b9e\u7528\u7684\u65b9\u6cd5\uff0c\u5b83\u53ef\u4ee5\u5224\u65ad\u8bf7\u6c42\u5185\u5bb9\u662f\u5426\u6ee1\u8db3\u9700\u6c42\uff0c\u6211\u4eec\u53ef\u4ee5\u653e\u5fc3\u4f7f\u7528\u901a\u8fc7 `verification()` \u65b9\u6cd5\u68c0\u67e5\u540e\u7684\u6570\u636e\u3002\u800c `DBConnect` \u7c7b\u7684 `write_one_docu()` \u65b9\u6cd5\u5219\u7528\u4e8e\u5199\u5165\u5355\u6761\u6587\u6863\uff0c\u5b83\u63a5\u6536\u4e00\u4e2a\u5b57\u5178\uff0c\u800c\u6070\u597d `verification()` \u65b9\u6cd5\u8fd4\u56de\u7684\u5c31\u662f\u4e00\u4e2a\u5b57\u5178\uff0c\u6240\u4ee5\u53ef\u4ee5\u76f4\u63a5\u4f5c\u4e3a\u6570\u636e\u5199\u5165\u3002\n\n#### \u67e5\u8be2\u6570\u636e\n\n\u4fee\u6539\u7ec4\u4ef6\u9879\u76ee\u7684 **GET** \u90e8\u5206\u4ee3\u7801\u3002\n\n```python\n    if parameter.method == 'GET':\n        find_data = db_books.find_paging(parameter)\n        return find_data\n```\n\n\u73b0\u5728\uff0c\u6211\u4eec\u67e5\u8be2 `books` \u96c6\u5408\u4e2d\u7684\u6240\u6709\u56fe\u4e66\u6587\u6863\u3002\n\n![\u6570\u636e\u5e93\u8bfb\u519902](/static_image/\u6570\u636e\u5e93\u8bfb\u519902.png)\n\n\u4e0a\u9762 `DBConnect` \u7c7b\u7684 `find_paging()` \u540c\u6837\u662f\u4e00\u4e2a\u7279\u522b\u5b9e\u7528\u7684\u65b9\u6cd5\uff0c\u5982\u4e0a\u9762\u6240\u89c1\uff0c\u4f60\u53ea\u9700\u8981\u628a\u8bf7\u6c42\u5185\u5bb9\u4ea4\u7ed9\u5b83\uff0c\u5b83\u4f1a\u76f4\u63a5\u8fd4\u56de\u67e5\u8be2\u7ed3\u679c\u7ed9\u4f60\u3002\n\n\u4f60\u8fd8\u53ef\u4ee5\u8bd5\u8bd5\u591a\u5199\u5165\u51e0\u4e2a\u6587\u6863\uff0c\u7136\u540e\u8bf7\u6c42 `http://127.0.0.1:5000/api/v1/demo?author=\u674e\u6770` \u63a5\u53e3\uff0c\u770b\u770b\u4f1a\u6709\u4ec0\u4e48\u795e\u5947\u7684\u6548\u679c\uff1f\n\n#### \u66f4\u65b0\u6570\u636e\n\n\u4fee\u6539\u7ec4\u4ef6\u9879\u76ee\u7684 **PUT** \u90e8\u5206\u4ee3\u7801\u3002\n\n```python\n    elif parameter.method == 'PUT':\n        param = parameter.verification(\n            checking=parameter.param_json,\n            verify={'id': str, 'title': str, 'author': str}\n        )\n        update_result = db_books.update_docu(\n            find_docu={'id': param['id']},\n            modify_docu={'title': param['title'], 'author': param['author']}\n        )\n        return update_result\n```\n\n\u73b0\u5728\uff0c\u6211\u4eec\u66f4\u65b0\u6307\u5b9a `id` \u7684 `books` \u96c6\u5408\u4e2d\u7684\u56fe\u4e66\u6587\u6863\u3002\n\n![\u6570\u636e\u5e93\u8bfb\u519903](/static_image/\u6570\u636e\u5e93\u8bfb\u519903.png)\n\n\u4e0a\u9762 `DBConnect` \u7c7b\u7684 `update_docu()` \u65b9\u6cd5\u7528\u4e8e\u66f4\u65b0\u6587\u6863\uff0c\u5b83\u4f1a\u8fd4\u56de\u66f4\u65b0\u7ed3\u679c\u3002\n\n#### \u5220\u9664\u6570\u636e\n\n\u4fee\u6539\u7ec4\u4ef6\u9879\u76ee\u7684 **DELETE** \u90e8\u5206\u4ee3\u7801\u3002\n\n```python\n    elif parameter.method == 'DELETE':\n        param = parameter.verification(\n            checking=parameter.param_json,\n            verify={'id': str}\n        )\n        delete_result = db_books.delete_docu(find_docu=param)\n        return delete_result\n```\n\n\u73b0\u5728\uff0c\u6211\u4eec\u5220\u9664\u6307\u5b9a `id` \u7684 `books` \u96c6\u5408\u4e2d\u7684\u56fe\u4e66\u6587\u6863\u3002\n\n![\u6570\u636e\u5e93\u8bfb\u519904](/static_image/\u6570\u636e\u5e93\u8bfb\u519904.png)\n\n\u4e0a\u9762 `DBConnect` \u7c7b\u7684 `delete_docu()` \u65b9\u6cd5\u7528\u4e8e\u5220\u9664\u6587\u6863\uff0c\u5b83\u4f1a\u8fd4\u56de\u5220\u9664\u7ed3\u679c\u3002\n\n### \u5efa\u7acb\u7ec4\u4ef6\u4f9d\u8d56\n\n\u5b8c\u6210\u4e00\u4e2a\u7b80\u5355\u7684\u56fe\u4e66\u7ba1\u7406\u7ec4\u4ef6\u540e\uff0c\u6211\u4eec\u8981\u5c06\u7ec4\u4ef6\u90e8\u7f72\u5230 Rainbond \u5e73\u53f0\uff0c\u5728 Rainbond \u5e94\u7528\u4e2d\u9009\u62e9 *\u5207\u6362\u5230\u7f16\u8f91\u6a21\u5f0f* \u8fdb\u5165\u4f9d\u8d56\u5173\u7cfb\u7f16\u8f91\u9875\u9762\uff0c\u5c06 *\u8ba1\u7b97\u7ec4\u4ef6* \u4f9d\u8d56\u5230 *\u5b58\u50a8\u7ec4\u4ef6* \u5373 **Mongo DB** \u7ec4\u4ef6\u3002\n\n![\u5efa\u7acb\u7ec4\u4ef6\u4f9d\u8d5601](/static_image/\u5efa\u7acb\u7ec4\u4ef6\u4f9d\u8d5601.png)\n\n\u91cd\u65b0\u6784\u5efa *\u8ba1\u7b97\u7ec4\u4ef6* \u5373 **Demo Component** \u7ec4\u4ef6\uff0c\u5b8c\u6210\u6784\u5efa\u5e76\u6eda\u52a8\u66f4\u65b0\u540e\uff0c\u901a\u8fc7\u65e5\u5fd7\u53ef\u4ee5\u770b\u5230\u7ec4\u4ef6\u670d\u52a1\u5df2\u7ecf\u5728\u6b63\u5e38\u8fd0\u884c\u4e86\u3002\n\n![\u5efa\u7acb\u7ec4\u4ef6\u4f9d\u8d5602](/static_image/\u5efa\u7acb\u7ec4\u4ef6\u4f9d\u8d5602.png)\n\n\u9700\u8981\u6ce8\u610f\uff1a\u5728 Rainbond \u5e73\u53f0\u66f4\u6539\u7ec4\u4ef6\u4f9d\u8d56\u5173\u7cfb\u540e\uff0c\u76f8\u5173\u7684\u7ec4\u4ef6\u9700\u8981\u8fdb\u884c\u6eda\u52a8\u66f4\u65b0\u3002\n\n## \u8fdb\u9636\n\n### \u5f02\u5e38\u5904\u7406\n\n\u901a\u8fc7 `error_handler()` \u65b9\u6cd5\uff0c\u53ef\u4ee5\u5c06\u5e38\u7528\u7684 *4xx* \u548c *5xx* \u72b6\u6001\u7801\u7684\u5f02\u5e38\u54cd\u5e94\u59d4\u6258\u7ed9 **rainbond-python** \u5904\u7406\uff0c\u5b83\u4f1a\u81ea\u52a8\u6355\u83b7\u8fd9\u4e9b\u5f02\u5e38\u5e76\u8fd4\u56de\u54cd\u5e94\u3002\n\n```python\n......\nfrom flask import Flask, request\nfrom rainbond_python.error_handler import error_handler\n......\napp = Flask(__name__)\nerror_handler(app)\n......\n```\n\n#### \u8de8\u57df\u5904\u7406\n\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b `error_handler()` \u65b9\u6cd5\u901a\u8fc7 `flask_cors` \u5e93\u4e00\u952e\u5904\u7406\u4e86\u670d\u52a1\u7aef\u8de8\u57df\u95ee\u9898\uff0c\u5982\u679c\u9700\u8981\u8003\u8651\u5b89\u5168\u95ee\u9898\uff0c\u53ef\u4ee5\u901a\u8fc7 `error_handler(app, simple_cors=False)` \u53d6\u6d88\u8de8\u57df\u652f\u6301\uff0c\u540c\u65f6\uff0c\u8fd8\u53ef\u4ee5\u91cd\u5199\u8de8\u57df\u903b\u8f91\u3002\n\n```python\n......\nerror_handler(app, simple_cors=False)\nfrom flask_cors import CORS\nCORS(app, resources={r'/.*': {'origins': 'http://127.0.0.1:8888'}})\n......\n```\n\n#### \u4e1a\u52a1\u5f02\u5e38\n\n\u4f7f\u7528 `handle_abnormal()` \u65b9\u6cd5\u53ef\u4ee5\u4e3b\u52a8\u629b\u51fa\u4e1a\u52a1\u903b\u8f91\u5f02\u5e38\uff0c\u7b80\u5355\u793a\u4f8b\u5982\u4e0b\u3002\n\n```python\n......\nfrom rainbond_python.tools import handle_abnormal\n......\n@app.route('/api/v1/demo', methods=['GET'])\ndef api_demo():\n    parameter = Parameter(request)\n\n    if parameter.method == 'GET':\n        handle_abnormal(message='\u5f02\u5e38\u4fe1\u606f~~~', status=400)\n......\n```\n\n\u8fd9\u6837\uff0c\u5f53\u7528\u6237\u8bf7\u6c42\u65f6\uff0c\u54cd\u5e94\u5185\u5bb9\u5982\u4e0b\uff1a\n\n```json\n{\n    \"code\": 400,\n    \"message\": \"\u5f02\u5e38\u4fe1\u606f~~~\",\n    \"server_time\": 20210220143830000,\n    \"host_name\": \"Z0jli2o0d2ymott0ggs6m\",\n    \"host_ip\": \"128.19.80.115\"\n}\n```\n\n\u8fd8\u53ef\u4ee5\u901a\u8fc7 `header` \u53c2\u6570\u5b57\u5178\u8bbe\u7f6e\u54cd\u5e94\u5934\u5b57\u5178\uff0c\u901a\u8fc7 `other` \u53c2\u6570\u6dfb\u52a0\u9644\u52a0\u4fe1\u606f\u5b57\u5178\u3002\n\n```python\nhandle_abnormal(message='2333~~~', status=400, other={'key1': 'value1', 'key2': {'a': 1}})\n```\n\n\u8fd9\u6837\u5c31\u53ef\u4ee5\u5c06\u66f4\u8be6\u7ec6\u7684\u63d0\u793a\u4fe1\u606f\u544a\u77e5\u7528\u6237\uff0c\u54cd\u5e94\u5185\u5bb9\u5982\u4e0b\uff1a\n\n```json\n{\n    \"code\": 400,\n    \"message\": \"2333~~~\",\n    \"server_time\": 20210220144625000,\n    \"host_name\": \"Z0jli2o0d2ymott0ggs6m\",\n    \"host_ip\": \"128.19.80.115\",\n    \"key1\": \"value1\",\n    \"key2\": {\n        \"a\": 1\n    }\n}\n```\n\n\u5f53\u7136\uff0c\u6b63\u5e38\u7684\u4e1a\u52a1\u903b\u8f91\u4e5f\u53ef\u4ee5\u4f7f\u7528 `handle_abnormal()` \u65b9\u6cd5\u8fd4\u56de\uff0c\u4f46\u662f\u8003\u8651\u5230\u4e1a\u52a1\u903b\u8f91\u7684\u4e0d\u786e\u5b9a\u6027\u548c\u590d\u6742\u6027\uff0c\u4e00\u822c\u5c06\u4f5c\u4e3a\u5f02\u5e38\u903b\u8f91\u7684\u54cd\u5e94\u4f7f\u7528\u3002\n\n## \u6570\u636e\u5907\u4efd\n\nmongo\u6570\u636e\u5e93\u7684\u5907\u4efd\uff0c\u662f\u901a\u8fc7\u5728rainbond\u5e73\u53f0mongo\u7ec4\u4ef6\u4e2d\u5b89\u88c5\u63d2\u4ef6\u7684\u65b9\u5f0f\u5b9e\u73b0\u7684\uff0c\u5e76\u4e14\u914d\u5408\u5f00\u6e90\u4e91\u5b58\u50a8\u670d\u52a1\uff0c\u5c06\u5907\u4efd\u6570\u636e\u4e0a\u4f20\u81f3\u4e91\u7aef\u3002\n\n#### \u63d2\u4ef6\u5b89\u88c5\u6b65\u9aa4\u5982\u4e0b\uff1a\n\n1.  ![\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef601](static_image/\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef601.png)\n3.  ![\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef602](static_image/\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef602.png)\n4.  ![\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef603](static_image/\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef603.png)\n5.  ![\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef604](static_image/\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef604.png)\n6.  ![\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef605](static_image/\u6570\u636e\u5e93\u5907\u4efd\u63d2\u4ef605.png)\n\n###### \u63d2\u4ef6\u53c2\u6570\u8bf4\u660e\n* \u5fc5\u987b\u53c2\u6570\uff1a\n\nMONGODB_HOST \uff1a \u6570\u636e\u5e93\u5730\u5740\n\nMONGODB_PORT \uff1a \u6570\u636e\u5e93\u7aef\u53e3\n\nMONGODB_DB \uff1a \u9700\u8981\u5907\u4efd\u7684\u6570\u636e\u5e93\u540d\u79f0\n\nFILE_DIR \uff1a \u5907\u4efd\u6570\u636e\u672c\u5730\u4fdd\u5b58\u76f8\u5bf9\u8def\u5f84\uff08\u540c\u65f6\u4e5f\u662f\u4e0a\u4f20\u5907\u4efd\u6570\u636e\u7684\u63a5\u53e3\u53c2\u6570\u4e4b\u4e00\uff09\n\nURL\uff1a \u4e0a\u4f20\u6570\u636e\u5907\u4efd\u63a5\u53e3\u5730\u5740\n\n* \u5b9a\u65f6\u4efb\u52a1\u53c2\u6570\uff08\u53ea\u652f\u6301\u4ee5\u4e0b\u53c2\u6570\uff09\n\nDAY : \u5929\u5faa\u73af\n\nDAY_OF_WEEK: \u5468\u671f\u5faa\u73af\n\nHOUR\uff1a\u5c0f\u65f6\u5faa\u73af\n\nMINUTE\uff1a\u5206\u949f\u5faa\u73af\n\nSECOND\uff1a\u79d2\u5faa\u73af\n\n\u76f8\u4f3c\u7528\u6cd5\u53ef\u53c2\u8003 https://www.jianshu.com/p/4c5bc85fc3fd\u4e2d2.1.3\u7ae0\u8282\uff0c\u7b2c\u4e09\u70b9 crontab\u89e6\u53d1\u5668\u7528\u6cd5\n\n#### \u5f00\u6e90\u4e91\u5b58\u50a8\u670d\u52a1\u5b89\u88c5\uff08\u53ef\u4f7f\u7528rainbond\u76f4\u63a5\u5b89\u88c5\u5373\u53ef\u4f7f\u7528\uff09\n\u53c2\u8003\u94fe\u63a5\uff1ahttps://www.laobuluo.com/2934.html\n\n### Parameter\n\n\u5904\u7406\u8bf7\u6c42\u4e0e\u54cd\u5e94\u53c2\u6570\u7684\u901a\u7528\u7c7b\u3002\n\n```python\nfrom rainbond_python.parameter import Parameter\n```\n\n#### \u83b7\u53d6\u8bf7\u6c42\u53c2\u6570\n\n\u901a\u8fc7 `Parameter` \u7c7b\u5b9e\u4f8b\uff0c\u53ef\u4ee5\u83b7\u53d6\u4ee5\u4e0b\u4fe1\u606f\uff1a\n\n- parameter.method: \u8bf7\u6c42\u7c7b\u578b\n- parameter.headers: \u8bf7\u6c42\u5934\n- parameter.param_url: URL\u4e2d\u4f20\u9012\u7684\u53c2\u6570\n- parameter.param_json: Json\u8bf7\u6c42\u4e2d\u7684\u53c2\u6570\n- parameter.param_form: \u8868\u5355\u8bf7\u6c42\u4e2d\u7684\u53c2\u6570\n\n\u6240\u6709\u4fe1\u606f\u5747\u4e3a\u5b57\u5178\u7c7b\u578b\uff0c\u901a\u8fc7 `json.dumps()` \u53ef\u4ee5\u76f4\u63a5\u4f5c\u4e3a\u54cd\u5e94\u8fd4\u56de\uff1a\n\n```python\n@app.route('/api/1.0/demo', methods=['GET', 'POST', 'PUT', 'DELETE'])\ndef api_demo():\n    parameter = Parameter(request)\n    if parameter.method == 'GET':\n        return json.dumps(parameter.param_url, ensure_ascii=False), 200, []\n    elif parameter.method == 'POST':\n        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []\n    elif parameter.method == 'PUT':\n        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []\n    elif parameter.method == 'DELETE':\n        return json.dumps(parameter.param_json, ensure_ascii=False), 200, []\n```\n\n#### \u6821\u9a8c\u53c2\u6570\u5185\u5bb9\n\n\u901a\u8fc7 `Parameter` \u7c7b\u7684 `verification()` \u65b9\u6cd5\uff0c\u53ef\u4ee5\u5224\u65ad\u53c2\u6570\u5b57\u5178\u662f\u5426\u7b26\u5408\u8981\u6c42\uff1a\n\n```python\n    elif parameter.method == 'POST':\n        param = parameter.verification(checking=parameter.param_json, verify={'name': str, 'age': int})\n```\n\n\u5176\u4e2d `checking` \u53c2\u6570\u662f\u9700\u8981\u6821\u9a8c\u7684\u53c2\u6570\u5b57\u5178\uff0c\u901a\u5e38\u4f20\u9012 `parameter.param_url`\u3001`parameter.param_json` \u6216 `parameter.param_form`\u3002\u7b2c\u4e8c\u4e2a `verify` \u53c2\u6570\u5219\u662f\u6821\u9a8c\u5185\u5bb9\u5b57\u5178\uff0c\u9700\u8981\u6307\u5b9a *\u53c2\u6570\u540d* \u548c *\u53c2\u6570\u7c7b\u578b* \u4f5c\u4e3a\u5b57\u5178\u9879\u3002\u5982\u679c\u8bf7\u6c42\u4e2d\u5305\u542b\u53ef\u9009\u53c2\u6570\uff0c\u53ef\u4ee5\u5c06\u8be5\u53c2\u6570\u7684\u540d\u79f0\u53ca\u5176\u9ed8\u8ba4\u503c\u8f93\u5165\u5230 `optional` \u53c2\u6570\u4e2d\uff0c\u4f8b\u5982\u53ef\u4ee5\u8bbe\u7f6e *age* \u53c2\u6570\u4e3a\u7a7a\u65f6\uff0c\u9ed8\u8ba4\u586b\u5145\u4e3a *18* \u5c81\uff1a\n\n```python\nparameter.verification(checking=parameter.param_json, verify={'name': str, 'age': int}, optional={'age': 18})\n```\n\n\u5982\u679c\u5224\u65ad\u5931\u8d25\uff0c\u5219\u76f4\u63a5\u8fd4\u56de\u5f02\u5e38\u54cd\u5e94\uff0c\u54cd\u5e94\u4f53\u4e2d\u5305\u542b\u660e\u786e\u7684\u63d0\u793a\u4fe1\u606f\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c`str` \u7c7b\u578b\u7684 \u5fc5\u9009\u53c2\u6570\u4e0d\u80fd\u4e3a\u7a7a\u5b57\u7b26\u4e32\uff0c\u5982\u679c\u9700\u8981\u4e3a\u7a7a\uff0c\u53ef\u4ee5\u901a\u8fc7 `null_value=True` \u8fdb\u884c\u8bbe\u7f6e\uff0c\u6216\u8005\u5c06\u5176\u4f5c\u4e3a\u53ef\u9009\u53c2\u6570\u5904\u7406\u3002\n\n#### \u6821\u9a8c\u6587\u4ef6\u8868\u5355\n\n\u5982\u679c\u9700\u8981\u63a5\u6536\u8868\u5355\u63d0\u4ea4\u7684\u6587\u4ef6\u5bf9\u8c61\uff0c\u53ef\u4ee5\u4f7f\u7528 `verification_file()` \u65b9\u6cd5\u5bf9\u8bf7\u6c42\u4e2d\u7684\u8868\u5355\u6587\u4ef6\u5b57\u6bb5\u8fdb\u884c\u6821\u9a8c\uff1a\n\n```python\n    elif parameter.method == 'POST':\n        param = parameter.verification(checking=parameter.param_form, verify={'id': str})\n        param_file = parameter.verification_file(verify_field=['updata'])\n```\n\n\u5982\u4e0a\u9762\u7684\u4ee3\u7801\uff0c\u5982\u679c\u8bf7\u6c42\u4e2d\u6ca1\u6709\u540d\u4e3a *updata* \u7684\u8868\u5355\u6587\u4ef6\u5b57\u6bb5\uff0c\u4f1a\u76f4\u63a5\u8fd4\u56de\u5f02\u5e38\u4fe1\u606f\u3002\u8be5\u65b9\u6cd5\u4e0e `verification()` \u65b9\u6cd5\u53ef\u4ee5\u540c\u65f6\u4f7f\u7528\u3002\u5982\u679c\u8fd8\u9700\u8981\u5224\u65ad\u4e0a\u4f20\u6587\u4ef6\u7684\u540e\u7f00\u540d\uff0c\u53ef\u4ee5\u901a\u8fc7 `verify_suffix` \u53c2\u6570\u8fdb\u884c\u914d\u7f6e\uff1a\n\n```python\nparam_file = parameter.verification_file(verify_field=['updata'], verify_suffix=['jpg'])\n# \u4e8c\u8005\u6548\u679c\u76f8\u540c\uff0c\u4f46\u662f\u5217\u8868\u7c7b\u578b\u53ef\u4ee5\u540c\u65f6\u6307\u5b9a\u591a\u4e2a\u540e\u7f00\u540d\u79f0\nparam_file = parameter.verification_file(verify_field=['updata'], verify_suffix=[['jpg']])\n```\n\n\u8be5\u65b9\u6cd5\u4f1a\u8fd4\u56de `werkzeug.datastructures.ImmutableMultiDict` \u5bf9\u8c61\uff0c\u5373\u901a\u8fc7 `request.files` \u83b7\u53d6\u5230\u7684\u5bf9\u8c61\uff0c\u63a5\u4e0b\u6765\u5c31\u53ef\u4ee5\uff1a\n\n- \u901a\u8fc7 `param_file.get('xxxx')` \u83b7\u53d6\u5230\u6587\u4ef6\u5bf9\u8c61\n- \u901a\u8fc7 `param_file.get('xxxx').filename` \u83b7\u53d6\u5177\u4f53\u6587\u4ef6\u540d\u79f0\n- \u901a\u8fc7 `param_file.get('xxxx').save('/xxx/xxx.jpg')` \u4fdd\u5b58\u6587\u4ef6\u5230\u672c\u5730\n\n### DBConnect\n\n\u5904\u7406 MongoDB \u8bfb\u5199\u884c\u4e3a\u7684\u901a\u7528\u7c7b\u3002\n\n```python\nfrom rainbond_python.db_connect import DBConnect\ndb = DBConnect(db='db_name', collection='collection_name')\n```\n\n#### \u5206\u9875\u67e5\u8be2\n\n\u652f\u6301 **GET** \u548c **POST** \u8bf7\u6c42\uff0c\u4f7f\u7528\u975e\u5e38\u7b80\u5355\uff0c\u76f4\u63a5\u628a `Parameter` \u7c7b\u7684\u5b9e\u4f8b\u4f20\u9012\u7ed9 `DBConnect` \u7c7b\u7684 `find_paging()` \u65b9\u6cd5\u5373\u53ef\uff1a\n\n```python\n@app.route('/api/1.0/demo', methods=['GET'])\ndef api_demo():\n    parameter = Parameter(request)\n    if parameter.method == 'GET':\n        find_data = db.find_paging(parameter)\n        return find_data, 200, []\n```\n\n\u5185\u90e8\u7ec4\u4ef6\u6216\u5916\u90e8\u5ba2\u6237\u7aef\u901a\u8fc7 */api/v1/demo?$offset=0&$limit=15&name=sb* \u5373\u53ef\u8bbf\u95ee\uff0c\u8bf7\u6c42\u53c2\u6570\u5982\u4e0b\uff1a\n\n- $limit: \u53ef\u9009\uff0c\u6307\u793a\u9875\u5927\u5c0f\uff0c\u4ece `1` \u5f00\u59cb\u8ba1\u7b97\uff0c\u9ed8\u8ba4 `10` \u6761\u6570\u636e\uff08\u5355\u7eaf\u7684\u6570\u6570\uff0c\u6570\u51e0\u4e2a\u5c31\u51e0\u4e2a\uff09\n- $offset: \u53ef\u9009\uff0c\u6307\u793a\u8bb0\u5f55\u8d77\u59cb\u4f4d\u7f6e\uff0c\u9ed8\u8ba4\u4ece `0` \u5f00\u59cb\u8ba1\u7b97\uff08\u4ee3\u7801\u903b\u8f91\uff0c\u7b2c\u51e0\u5c31\u662f\u4ece\u6570\u636e\u5e93\u7684\u7b2c\u51e0\u6761\u5f00\u59cb\u53d6\u6587\u6863\uff09\n- $orderby: \u53ef\u9009\uff0c\u6392\u5e8f\u89c4\u5219\uff08Eg: `key1 desc,key2 asc`\uff09\uff0casc=\u5347\u5e8f\u3001desc=\u964d\u5e8f\n- $start_date: \u53ef\u9009\uff0c\u5f00\u59cb\u65e5\u671f\uff08\u533a\u95f4\u67e5\u8be2\uff09\uff0c\u652f\u6301\u65e5\u671f\uff082020-10-1\uff09\u683c\u5f0f\u548c\u65f6\u95f4\u6233\uff08601481600\uff09\u683c\u5f0f\n- $end_date: \u53ef\u9009\uff0c\u7ed3\u675f\u65e5\u671f\uff08\u533a\u95f4\u67e5\u8be2\uff09\uff0c\u540c\u4e0a\uff0c\u5fc5\u987b\u6210\u5bf9\u51fa\u73b0\n- $date_type: \u53ef\u9009\uff0c\u533a\u95f4\u67e5\u8be2\u7684\u5b57\u6bb5\uff0c\u9ed8\u8ba4\u4e3a\u66f4\u65b0\u65f6\u95f4\uff08`update_time`\uff09\uff0c\u53ef\u4ee5\u8bbe\u7f6e\u6210\u6309\u521b\u5efa\u65f6\u95f4\uff08`creation_time`\uff09\u67e5\u8be2\n- <\u4efb\u610f\u7b5b\u9009\u5b57\u6bb5>: \u53ef\u9009\uff0c\u4efb\u610f\u7b5b\u9009\u503c\n\n\u54cd\u5e94\u5b57\u6bb5\u5982\u4e0b\uff1a\n\n- total: \u67e5\u8be2\u5230\u7684\u6587\u6863\u603b\u6570\n- items: \u6587\u6863\u5217\u8868\n- dummy_remove: \u5047\u5220\u9664\u6587\u6863\u6570\uff08\u4e0d\u53c2\u4e0e\u67e5\u8be2\uff09\n\n\u7531\u4e8e\u6280\u672f\u539f\u56e0\uff0c\u7b5b\u9009\u5b57\u6bb5\u76ee\u524d\u4e0d\u652f\u6301 `int` \u578b\u6570\u636e\u7684\u6a21\u7cca\u67e5\u8be2\u3002\u540c\u65f6 `$start_date` \u548c `$end_date` \u5982\u679c\u4f20\u9012\u7684\u662f\u65f6\u95f4\u6233\u683c\u5f0f\uff0c\u80fd\u7cbe\u786e\u5230\u79d2\u3002\uff08\u66f4\u590d\u6742\u7684\u67e5\u8be2\uff0c\u53ef\u4ee5\u5c06\u67e5\u8be2\u503c\u63d0\u524d\u5904\u7406\u6210\u5b57\u5178\uff0c\u8fd9\u6837\u53ef\u4ee5\u5b9e\u73b0\u4e00\u4e9b\u975e\u5e38\u89c4\u6027\u7684\u9700\u6c42\uff09\n\n#### \u5199\u6587\u6863\n\n##### \u5199\u5165\u5355\u4e2a\u6587\u6863\n\n```python\ninsert_dict = {'name': 'Xiao Ming', 'age': 23}\ndb.write_one_docu(docu=insert_dict)\n```\n\n\u5982\u679c\u5199\u5165\u5931\u8d25\uff0c\u4f1a\u76f4\u63a5\u8fd4\u56de\u5f02\u5e38\u54cd\u5e94\uff0c\u5982\u679c\u6210\u529f\u5219\u4f1a\u8fd4\u56de\u65b0\u6570\u636e\u7684 `_id` \u503c\u3002\n\n##### \u5199\u5165\u591a\u4e2a\u6587\u6863\n\n```python\ninsert_dict_list = [{'name': 'Xiao Ming', 'age': 23},{'name': 'lao Yang', 'age': 35}]\ndb.write_many_docu(docu_list=insert_dict_list)\n```\n\n\u5982\u679c\u5199\u5165\u5931\u8d25\uff0c\u4f1a\u76f4\u63a5\u8fd4\u56de\u5f02\u5e38\u54cd\u5e94\uff0c\u5982\u679c\u6210\u529f\u5219\u4f1a\u8fd4\u56de\u65b0\u6570\u636e\u7684 `_id`\u503c\u7684\u5217\u8868\u3002\n\n#### \u6587\u6863\u662f\u5426\u5b58\u5728\n\n```python\nexamine_dict = {'name': 'Xiao Ming'}\nif db.does_it_exist(docu=examine_dict):\n    print('Docu already exists')\nelse:\n    print('Docu does not exist')\n```\n\n#### \u66f4\u65b0\u6587\u6863\n\n\u540c\u6837\u7684\uff0c\u5982\u679c\u66f4\u65b0\u5931\u8d25\uff0c\u4e5f\u4f1a\u76f4\u63a5\u8fd4\u56de\u5f02\u5e38\u54cd\u5e94\u3002\n\n##### \u66f4\u65b0\u5355\u4e2a\u5339\u914d\u6587\u6863\n\n```python\nfind_dict = {'name': 'Xiao Ming'}\nmodify_dict = {'name': 'Xiao Hong'}\ndb.update_docu(find_docu=find_dict, modify_docu=modify_dict)\n```\n\n##### \u66f4\u65b0\u5168\u90e8\u5339\u914d\u6587\u6863\n\n```python\nfind_dict = {'age': 23}\nmodify_dict = {'name': '23 year old'}\ndb.update_docu(find_docu=find_dict, modify_docu=modify_dict, many=True)\n```\n\n\u8be5\u65b9\u6cd5\u4f1a\u8fd4\u56de\u4e00\u4e2a\u5305\u542b `matched_count` \u548c `modified_count` \u5373\u5339\u914d/\u5f71\u54cd\u6570\u636e\u6761\u6570\u7684\u5b57\u5178\u3002\n\n\n#### \u66f4\u65b0\u6587\u6863-\u6307\u5b9a\u5b57\u6bb5\u81ea\u589e\n\n```python\nfind_dict = {'name': 'xiao yang'}\nmodify_dict = {'age': 1}\ndb.update_docu_inc(find_docu=find_dict, modify_docu=modify_dict)\n```\n\u8868\u793aage\u5b57\u6bb5\u6570\u5b57\u589e\u52a01\uff0c\u539f\u5148age=18\uff0c\u8c03\u7528\u8be5\u65b9\u6cd5\u540e\uff0cage=19\n\u8be5\u65b9\u6cd5\u4f1a\u8fd4\u56de\u4e00\u4e2a\u5305\u542b `matched_count` \u548c `modified_count` \u5373\u5339\u914d/\u5f71\u54cd\u6570\u636e\u6761\u6570\u7684\u5b57\u5178\u3002\n\n#### \u5220\u9664\u6587\u6863\n\n\u5220\u9664\u6587\u6863\u5206\u4e3a **\u771f\u5220\u9664** \u548c **\u5047\u5220\u9664** \u4e24\u79cd\u65b9\u5f0f\uff0c\u901a\u8fc7 `delete_docu()` \u65b9\u6cd5\u5b9e\u73b0\uff0c\u8be5\u65b9\u6cd5\u4f1a\u8fd4\u56de\u4e00\u4e2a\u5305\u542b `deleted_count` \u548c `false_delete` \u7684\u5b57\u5178\u3002\u3002\n\n##### \u771f\u5220\u9664\u6587\u6863\n\n```python\ndb.delete_docu(find_docu={'id': '60053fa139842d28d7563c6c'})\n```\n\n##### \u5047\u5220\u9664\u6587\u6863\n\n```python\ndb.delete_docu(find_docu={'id': '60053fa139842d28d7563c6c'}, false_delete=True)\n```\n\n\u5047\u5220\u9664\u64cd\u4f5c\u4f1a\u5728\u5bf9\u5e94\u7684\u6587\u6863\u4e2d\u6dfb\u52a0\u4e00\u4e2a `remove_time` \u5b57\u6bb5\uff0c\u91cc\u9762\u8bb0\u5f55\u8fd9\u4e2a\u6587\u6863\u88ab\u79fb\u9664\u7684\u65f6\u95f4\u3002\n\n##### \u6279\u91cf\u5220\u9664\u6587\u6863\n\n```python\ndb.delete_docu(find_docu={'id': {'$in': ['111', '222']}}, many=True)\n```\n\n#### \u67e5\u8be2\u6587\u6863\n\n\u901a\u8fc7 `find_docu()` \u6807\u51c6\u67e5\u8be2\u65b9\u6cd5\u65f6\uff0c\u65e0\u8bba\u67e5\u8be2\u5355\u4e2a\u8fd8\u662f\u591a\u4e2a\uff0c\u8fd4\u56de\u5747\u662f `list` \u7c7b\u578b\u6570\u636e\uff0c\u6ca1\u6709\u5339\u914d\u6570\u636e\u65f6\u8fd4\u56de\u7a7a\u5217\u8868\u3002\n\n##### \u67e5\u8be2\u5355\u4e2a\u5339\u914d\u6587\u6863\n\n```python\nfind_dict = {'title': {'$regex': '\u6807\u9898'}}\nfind_data_list = db.find_docu(find_dict=find_dict, many=False)\nprint(find_data_list[0])\n```\n\n##### \u67e5\u8be2\u5168\u90e8\u5339\u914d\u6587\u6863\n\n```python\nfind_dict = {'title': {'$regex': '\u6807\u9898'}}\nfind_data_list = db.find_docu(find_dict=find_dict)\nfor find_data in find_data_list:\n    print(find_data)\n```\n\n##### \u6839\u636eid\u67e5\u627e\u6587\u6863\n\n```python\nfrom rainbond_python.db_connect import DBConnect\ndb = DBConnect('unitest_rainbond_python', 'test_db_connect')\nid = db.write_one_docu({'name': 'LaoXu'})\ndocu = db.find_docu_by_id(str(id))\n\n# \u5f53id\u4e0d\u5b58\u5728\u65f6\uff0c\u9ed8\u8ba4\u4f1a\u4f7f\u7528abort\u629b\u51fa\u5f02\u5e38\nfail_docu = db.find_docu_by_id('6008daa19223551b00548ded')\n# \u53ef\u4ee5\u5c06raise_err=False\u65f6\uff0cid\u4e0d\u5b58\u5728\u4f1a\u8fd4\u56deNone\nfail_docu = db.find_docu_by_id('6008daa19223551b00548ded',raise_err=False)\n```\n\n\u8be5\u65b9\u6cd5\u8fd4\u56de\u8bb0\u5f55\u5b57\u5178\uff0c\u4e14\u628a'_id'\u8f6c\u6362\u4e3a\u4e86str\u7c7b\u578b\n\n##### \u6839\u636eid\u5217\u8868\u67e5\u627e\u6587\u6863\n\n```python\nfrom rainbond_python.db_connect import DBConnect\ndb = DBConnect('unitest_rainbond_python', 'test_db_connect')\ndocu_list = db.find_docu_by_id_list(['6008daa19223551b00548ded','6008daa29223551b00548dee'])\n```\n\n\u8be5\u65b9\u6cd5\u8fd4\u56de\u8bb0\u5f55\u5b57\u5178\u5217\u8868\uff0c\u4e14\u628a'_id'\u8f6c\u6362\u4e3a\u4e86str\u7c7b\u578b\u3002\u5f53\u6240\u6709id\u4e0d\u5b58\u5728\u65f6\uff0c\u8fd4\u56de[]\n\n#### \u6839\u636e\u5b57\u6bb5\u53bb\u91cd\u67e5\u8be2\u6587\u6863\n\n```python\nnum = find_docu_distinct('age')\n``````\n\u8fd4\u56deint\u7c7b\u578b\u6570\u5b57\uff0c\u4ee3\u8868\u6587\u6863\u4e2dage\u5b57\u6bb5\u53bb\u91cd\u540e\u7684\u6570\u91cf\n\n### \u6587\u4ef6\u4e0b\u8f7d\n\n\u5728\u7f51\u7edc\u4e0a\u4f20\u8f93\u6587\u4ef6\uff0c\u76ee\u524d\u4e3b\u8981\u6709\u4e0b\u8f7d\u548c\u6d41\u5f0f\u4f20\u8f93\u4e24\u79cd\u65b9\u6848\uff0c\u5206\u522b `rainbond_python.download` \u5305\u7684\u5bf9\u5e94 `download_file()` \u548c `download_flow()` \u65b9\u6cd5\u3002\n\n#### \u666e\u901a\u4e0b\u8f7d\n\n\u901a\u5e38\u7528\u4e8e\u6587\u6863\u6587\u4ef6\uff08\u538b\u7f29\u5305/PDF/TXT\u7b49\u6587\u6863\uff09\uff0c\u8fd9\u79cd\u65b9\u5f0f\u5fc5\u987b\u7b49\u5168\u90e8\u5185\u5bb9\u4f20\u8f93\u5b8c\u6bd5\u540e\uff0c\u624d\u80fd\u5728\u672c\u5730\u673a\u5668\u6253\u5f00\uff1a\n\n```python\n......\nfrom rainbond_python.download import download_file\n......\n    if parameter.method == 'GET':\n        download_response = download_file(file_path='C:/Users/xxx/Desktop', file_name='\u65b0\u5efa\u6587\u672c\u6587\u6863.txt'])\n        return download_response\n......\n```\n\n#### \u6d41\u5f0f\u4f20\u8f93\n\n\u901a\u5e38\u7528\u4e8e\u591a\u5a92\u4f53\u6587\u4ef6\uff08\u89c6\u9891/\u97f3\u9891/\u76f4\u64ad\u6d41\u7b49\u573a\u666f\uff09\uff0c\u6587\u4ef6\u4fe1\u606f\u7531\u670d\u52a1\u5668\u5411\u7528\u6237\u8ba1\u7b97\u673a\u8fde\u7eed\u5b9e\u65f6\u5730\u4f20\u9001\uff0c\u4e0d\u5fc5\u7b49\u5230\u6574\u4e2a\u6587\u4ef6\u5168\u90e8\u4e0b\u8f7d\u5b8c\u6bd5\uff0c\u901a\u5e38\u7ecf\u8fc7\u51e0\u79d2\u6216\u5341\u51e0\u79d2\u7684\u542f\u52a8\u5ef6\u65f6\u5373\u53ef\u6253\u5f00\uff1a\n\n```python\n......\nfrom rainbond_python.download import download_flow\n......\n    if parameter.method == 'GET':\n        download_response = download_flow(file_path='C:/Users/xxx/Desktop', file_name='\u5fae\u89c6\u9891.mp4'])\n        return download_response\n......\n```\n\n#### \u6253\u5305\u76ee\u5f55\u751f\u6210zip\u6587\u4ef6\u4e0b\u8f7d\uff08\u5728\u5185\u5b58\u4e2d\u6253\u5305\uff09\n\n\u901a\u5e38\u7528\u4e8e\u5c06\u73b0\u6709\u76ee\u5f55\u6587\u4ef6\uff0c\u6253\u5305\u6210zip\u6587\u4ef6\uff0c\u63d0\u4f9b\u7528\u6237\u4e0b\u8f7d\u3002\u6253\u5305zip\u6587\u4ef6\u6570\u636e\u5728\u5185\u5b58\u4e2d\u5b8c\u6210\uff0c\u5b8c\u6210\u540e\u4ece\u5185\u5b58\u4e2d\u8bfb\u53d6\u4e8c\u8fdb\u5236\u6570\u636e\uff0c\u5e76\u4e14\u56de\u6536\u5185\u5b58\u3002\n\u53c2\u6570save_zip\u8868\u793a\u672c\u5730\u662f\u5426\u5b58\u50a8\u6253\u5305zip\u6587\u4ef6\uff0c\u9ed8\u8ba4\u503c\u4e3a False\uff0c\u4e3aTrue\u65f6,\u6253\u5305\u6587\u4ef6\u4fdd\u5b58\u5728\u6253\u5305\u76ee\u5f55\u540c\u7ea7\n\n```python\nfrom rainbond_python.download import download_directory\n\n......\n    if parameter.method == 'GET':\n        download_response = download_directory(dir_path='C:/Users/xxx/project', zip_name='project.zip'],save_zip=False)\n        return download_response\n......\n```\n\n### RedisConnect\n\n\u5904\u7406 Redis \u8bfb\u5199\u884c\u4e3a\u7684\u901a\u7528\u7c7b\u3002\n\n```python\nfrom rainbond_python.redis_connect import RedisConnect\nredis_connect = RedisConnect(db=0)\n```\n\n### \u6743\u9650\u8ba4\u8bc1\n\n\u8ba4\u8bc1\u4e2d\u5fc3\u7ec4\u4ef6\u4e4b\u524d\uff0c\u9700\u8981\u5c06\u7ec4\u4ef6\u4f9d\u8d56\u4e8e **\u8ba4\u8bc1\u4e2d\u5fc3**\u3001**Redis** \u548c **MongoDB** \u7ec4\u4ef6\uff0c\u7136\u540e\u5728\u4e1a\u52a1\u4ee3\u7801\u4e2d\u7f16\u5199\u5982\u4e0b\u4ee3\u7801\uff0c\u5b8c\u6210\u63a5\u5165\uff1a\n\n```python\n......\nfrom flask import Flask, request, session\nfrom rainbond_python.verify_token import VerifyToken, set_token_session\n......\n# \u6ce8\u518c\u6743\u9650\u4fe1\u606f\u5230\u8ba4\u8bc1\u4e2d\u5fc3\u7ec4\u4ef6\nper_defaults = [\n    {'session_key': 'auth_xxxx', 'center_name': '\u7ec4\u4ef6\u540d\u79f0', 'permission_name': '\u81ea\u5b9a\u4e49\u6743\u9650', 'status': [0, 1, 2]},\n]\ntoken = VerifyToken(per_defaults=per_defaults)\nset_token_session(app, verify=token, per_defaults=per_defaults)\n......\n@app.route('/api/v1/demo', methods=['GET'])\ndef api_v1_demo():\n    # \u83b7\u53d6\u7528\u6237\u6743\u9650\uff1a0-1-2-4-8-16-32-64 (\u65e0\u6743\u9650-\u67e5\u770b-\u65b0\u589e-\u7f16\u8f91-\u5220\u9664-\u4e0b\u8f7d-\u2026\u2026)\n    # Eg: [0, 1, 2]\n    permission_list = session['auth_reports']['permission_list']\n\n    if parameter.method == 'GET' and (1 in permission_list):  # \u7528\u6237\u6709\u67e5\u8be2\u6743\u9650\n        # \u901a\u8fc7\u8ba4\u8bc1\u5e76\u7b26\u5408\u6743\u9650\u540e\uff0c\u53ef\u4ee5\u6267\u884c\u4e1a\u52a1\u903b\u8f91\n        psss\n......\n```\n\n\u6ce8\u518c\u6743\u9650\u4fe1\u606f\u5230\u8ba4\u8bc1\u4e2d\u5fc3\u7ec4\u4ef6\u7684\u65f6\u5019\uff0c`center_name` \u548c `permission_name` \u5efa\u8bae\u90fd\u4ee5\u4e2d\u6587\u547d\u540d\uff0c\u56e0\u4e3a\u524d\u7aef\u7ba1\u7406\u9875\u9762\u4f1a\u76f4\u63a5\u8bfb\u53d6\u8fd9\u4e24\u4e2a\u5b57\u6bb5\u3002`session_key` \u662f\u5199\u5230\u4f1a\u8bdd\u4e2d\uff0c\u5efa\u8bae\u4f7f\u7528\u82f1\u6587\u547d\u540d\u3002\u7136\u540e\u6839\u636e\u4e0a\u9762\u7684\u6ce8\u518c\u5185\u5bb9\uff0c\u6211\u4eec\u53ef\u4ee5\u4ece\u4f1a\u8bdd\u4e2d\u83b7\u53d6\u7528\u6237\u8ba4\u8bc1\u4fe1\u606f\uff1a\n\n- session['auth_xxxx']['permission_list']: 0-1-2-4-8-16-32-64 (\u65e0\u6743\u9650-\u67e5\u770b-\u65b0\u589e-\u7f16\u8f91-\u5220\u9664-\u4e0b\u8f7d-\u2026\u2026)\n- session['auth_xxxx']['is_all_data']: \u524d\u7528\u6237\u662f\u5426\u80fd\u67e5\u770b\u5168\u90e8\u6570\u636e\n- session['real_name']: \u8d26\u6237\u771f\u5b9e\u59d3\u540d\n- session['user_name']: \u8d26\u6237\u540d\u79f0\n- session['token_id']: \u8d26\u6237ID\n\n\u5982\u679c\u9700\u8981\u6307\u5b9a\u67d0\u4e9b\u8def\u7531\u4e0d\u53c2\u4e0e\u8ba4\u8bc1\uff0c\u53ef\u4ee5\u901a\u8fc7 `set_token_session` \u65b9\u6cd5\u7684 `whitelist` \u53c2\u6570\u8bbe\u7f6e\u767d\u540d\u5355\uff0c\u4f8b\u5982 `whitelist=['/api/v1/demo']` \u5219\u8868\u793a\u8be5\u8def\u7531\u53ef\u4ee5\u968f\u610f\u88ab\u8bf7\u6c42\u3002\n\n### \u901a\u7528\u65b9\u6cd5\n\n#### handle_date()\n\n\u5c06 *2020-10-1* \u6216 *601481600* \u5373\u65e5\u671f\u683c\u5f0f\u6216\u65f6\u95f4\u6233\u683c\u5f0f\u7684\u5b57\u7b26\u4e32\uff0c\u5904\u7406\u6210 Python \u7684 `datetime.datetime` \u6570\u636e\uff1a\n\n```python\nfrom rainbond_python.tools import handle_date\nprint(handle_date(date='2020-10-1'))\nprint(handle_date(date='2020-10-31', date_type='end'))\n```\n\n\u901a\u8fc7 `date_type` \u53ef\u4ee5\u8bbe\u7f6e\u662f\u65e5\u671f\u7684\u5f00\u59cb\uff08`start`\uff09\u8fd8\u662f\u4e00\u5929\u7684\u7ed3\u675f\uff08`end`\uff09\u65f6\u95f4\u3002\n\n#### handle_db_dict()\n\n\u5c06 MongoDB \u5b57\u5178\u6570\u636e\u4e2d\u7684 `_id` \u8f6c\u6362\u4e3a `str` \u7c7b\u578b\u3001\u65f6\u95f4\u8f6c\u6362\u6210\u65f6\u95f4\u6233\uff1a\n\n```python\nquery_dict = self.mongo_collection.find_one({'title': {'$regex': '\u6807\u98981'}})\nhandle_db_dict(query_dict)\n```\n\n#### handle_db_to_list()\n\n\u5c06 MongoDB \u7684\u5217\u8868\u4e2d\u7684 `_id` \u8f6c\u6362\u4e3a `str` \u7c7b\u578b\uff0c\u5e76\u8f6c\u6362\u4e3a\u5b57\u5178\u5217\u8868\uff08\u539fdb\u7684id\u662fObjectId\u7c7b\u578b\uff0c\u8f6c\u4e3ajson\u4f1a\u62a5\u9519\uff09\uff1a\n\n```python\nfrom rainbond_python.tools import handle_db_to_list\nfrom rainbond_python.db_connect import DBConnect\n\ndef test_handle_db_to_list():\n    db = DBConnect('unitest_rainbond_python', 'test_parameter')\n    old_list = db.mongo_collection.find({})\n    new_list = handle_db_to_list(old_list)\n    print('new_list is a list of dict',new_list)\n```\n\n#### handle_time_difference()\n\n\u8ba1\u7b97\u524d\u7aef\u4f20\u9012\u7684\u4e24\u4e2a\u65f6\u95f4\u6233\u4e4b\u95f4\uff0c\u76f8\u5dee\u591a\u5c11\u79d2\uff0c\u8fd4\u56de `float` \u7c7b\u578b\uff0c\u89e3\u51b3\u524d\u540e\u7aef\u65f6\u95f4\u6233\uff08\u524d\u7aef\u7684\u6beb\u79d2\u662f\u6574\u6570\u4f4d\u3001Python\u7684\u6beb\u79d2\u662f\u5c0f\u6570\u4f4d\uff09\u5dee\u5f02\u95ee\u9898\uff1a\n\n```python\nfrom rainbond_python.tools import handle_time_difference\nhandle_time_difference(start_timestamp=1614051008, end_timestamp=1614051008)\n```\n\n## \u5f00\u53d1\u4e0e\u6d4b\u8bd5\n\n### \u8c03\u8bd5\u5f00\u53d1\n\n\u57fa\u7840\u8c03\u8bd5\u4ee3\u7801\u7684 `demo.py` \u5373 *rainbond -c demo-component* \u547d\u4ee4\u521b\u5efa\u9879\u76ee\u4e2d\u7684 `app.py` \u6587\u4ef6\uff0c\u662f\u4e00\u4e2a\u53ef\u4ee5\u5feb\u901f\u5f00\u59cb\u7684\u57fa\u7840\u4ee3\u7801\u9879\u76ee\u3002\n\n\u5728\u672c\u5730\u8c03\u8bd5\u65f6\uff0c\u5728 **demos** \u76ee\u5f55\u4e0b\u521b\u5efa `dev_xxxxx.py`\uff0c\u5e76\u590d\u5236 `demo.py` \u6587\u4ef6\u91cc\u7684\u4ee3\u7801\uff0c\u5e76\u5728\u91cc\u9762\u8c03\u8bd5 *rainbond_python* \u76ee\u5f55\u4e0b\u7684\u4ee3\u7801\u3002\uff08\u672c\u5730\u521b\u5efa\u7684 dev_*.py \u6587\u4ef6\u4f1a\u88ab\u5ffd\u7565\uff0c\u4e0d\u4f1a\u88ab\u63d0\u4ea4\uff09\uff0c\u540c\u65f6\u8981\u5728\u5f00\u5934\u5904\u6dfb\u52a0\u4e0b\u9762\u4ee3\u7801\uff0c\u4ee5\u8c03\u7528\u57fa\u7840\u5305\u4e2d\u7684\u4ee3\u7801\uff1a\n\n```python\n......\nimport sys\nsys.path.append('..')\nfrom rainbond_python.parameter import Parameter\nfrom rainbond_python.error_handler import error_handler\nfrom rainbond_python.db_connect import DBConnect\n......\n```\n\n### \u5355\u5143\u6d4b\u8bd5\n\n\u5355\u5143\u6d4b\u8bd5\u5728 /tests/* \u76ee\u5f55\u4e0b\n\n* \u6267\u884c\u5355\u5143\u6d4b\u8bd5\n```shell script\n$ pytest\n```\n\n## \u53c2\u8003\n\n- [Restful API](https://www.runoob.com/w3cnote/restful-architecture.html) : \u5177\u4f53\u7684\u7ec4\u4ef6API\u5f00\u53d1\u6807\u51c6\n- [12 Factor](https://12factor.net/zh_cn/) : \u7b26\u5408\u5341\u4e8c\u8981\u7d20\u7684\u624d\u662f\u4e91\u539f\u751f\u5e94\u7528\n- [RainBond](https://www.rainbond.com/docs/) : \u4e00\u4e2a\u5f00\u6e90\u7684\u4e91\u539f\u751f\u5e73\u53f0",
    "bugtrack_url": null,
    "license": "",
    "summary": "Rainbond python cloud native development base library",
    "version": "1.3.6",
    "split_keywords": [
        "rainbond",
        "python",
        "cloud",
        "native"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "47ddb00cb5e07894cc6363aef733220b",
                "sha256": "e868c0c3fc863e33ca8f3a66d6c951743a91d62f265f390c04adff677bc7867b"
            },
            "downloads": -1,
            "filename": "rainbond-python-1.3.6.tar.gz",
            "has_sig": false,
            "md5_digest": "47ddb00cb5e07894cc6363aef733220b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 43459,
            "upload_time": "2022-12-07T08:30:31",
            "upload_time_iso_8601": "2022-12-07T08:30:31.163511Z",
            "url": "https://files.pythonhosted.org/packages/a5/a5/fa96323e60ac6fd57b7e6783fd6161076dfe25589fe02bf10b420ec1e30f/rainbond-python-1.3.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-12-07 08:30:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "hekaiyou",
    "github_project": "rainbond-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "rainbond-python"
}
        
Elapsed time: 0.02175s