diff --git a/develop/dev-guide-sample-application-python-peewee.md b/develop/dev-guide-sample-application-python-peewee.md index b3bd45193698..e1fcfae6e1e3 100644 --- a/develop/dev-guide-sample-application-python-peewee.md +++ b/develop/dev-guide-sample-application-python-peewee.md @@ -1,245 +1,294 @@ --- -title: TiDB 和 peewee 的简单 CRUD 应用程序 -summary: 给出一个 TiDB 和 peewee 的简单 CRUD 应用程序示例。 +title: 使用 peewee 连接到 TiDB +summary: 了解如何使用 peewee 连接到 TiDB。本文提供了使用 peewee 与 TiDB 交互的 Python 示例代码片段。 --- - - +# 使用 peewee 连接到 TiDB -# TiDB 和 peewee 的简单 CRUD 应用程序 +TiDB 是一个兼容 MySQL 的数据库。[peewee](https://docs.peewee-orm.com/) 为当前流行的开源 Python ORM (Object Relational Mapper) 之一。 -[peewee](http://docs.peewee-orm.com/en/latest/) 为当前比较流行的开源 Python ORM 之一。 +本文档将展示如何使用 TiDB 和 peewee 来完成以下任务: -本文档将展示如何使用 TiDB 和 peewee 来构造一个简单的 CRUD 应用程序。 +- 配置你的环境。 +- 使用 peewee 连接到 TiDB 集群。 +- 构建并运行你的应用程序。你也可以参考[示例代码片段](#示例代码片段),完成基本的 CRUD 操作。 -> **注意:** +> **注意** > -> 推荐使用 Python 3.10 及以上版本进行 TiDB 的应用程序的编写。 +> 本文档适用于 TiDB Serverless、TiDB Dedicated 和本地部署的 TiDB。 -## 第 1 步:启动你的 TiDB 集群 +## 前置需求 -本节将介绍 TiDB 集群的启动方法。 +- 推荐 [Python 3.8](https://www.python.org/downloads/) 及以上版本。 +- [Git](https://git-scm.com/downloads)。 +- TiDB 集群。如果你还没有 TiDB 集群,可以按照以下方式创建: + - (推荐方式)参考[创建 TiDB Serverless 集群](/develop/dev-guide-build-cluster-in-cloud.md#第-1-步创建-tidb-serverless-集群),创建你自己的 TiDB Cloud 集群。 + - 参考[部署本地测试 TiDB 集群](/quick-start-with-tidb.md#部署本地测试集群)或[部署正式 TiDB 集群](/production-deployment-using-tiup.md),创建本地集群。 -**使用 TiDB Serverless 集群** +## 运行代码并连接到 TiDB -详细步骤,请参考:[创建 TiDB Serverless 集群](/develop/dev-guide-build-cluster-in-cloud.md#第-1-步创建-tidb-serverless-集群)。 +本小节演示如何运行示例应用程序的代码,并连接到 TiDB。 -**使用本地集群** +### 第 1 步:克隆示例代码仓库到本地 -详细步骤,请参考:[部署本地测试 TiDB 集群](/quick-start-with-tidb.md#部署本地测试集群)或[部署正式 TiDB 集群](/production-deployment-using-tiup.md)。 +运行以下命令,将示例代码仓库克隆到本地: -## 第 2 步:获取代码 +```bash +git clone https://github.com/tidb-samples/tidb-python-peewee-quickstart.git +cd tidb-python-peewee-quickstart +``` + +### 第 2 步:安装依赖 + +运行以下命令,安装示例代码所需要的依赖(包括 peewee 和 PyMySQL): -```shell -git clone https://github.com/pingcap-inc/tidb-example-python.git +```bash +pip install -r requirements.txt ``` -此处将以 peewee **3.15.4** 版本进行说明。 +#### 为什么安装 PyMySQL? -```python -import os -import uuid -from typing import List +peewee 是一个支持多种数据库的 ORM 库。它是对数据库的高层抽象,可以帮助开发者以更面向对象的方式编写 SQL 语句。但 peewee 并不提供数据库驱动,因此需要单独安装用于连接 TiDB 的驱动。本示例项目使用 PyMySQL 作为数据库驱动。PyMySQL 是一个与 TiDB 兼容的纯 Python 实现的 MySQL 客户端库,并可以在所有平台上安装。更多信息,参考 [peewee 官方文档](https://docs.peewee-orm.com/en/latest/peewee/database.html?highlight=mysql#using-mysql)。 -from peewee import * +### 第 3 步:配置连接信息 -from playhouse.db_url import connect +根据不同的 TiDB 部署方式,使用不同的方法连接到 TiDB 集群。 -db = connect('mysql://root:@127.0.0.1:4000/test') + +
-class Player(Model): - id = CharField(max_length=36, primary_key=True) - coins = IntegerField() - goods = IntegerField() +1. 在 TiDB Cloud 的 [**Clusters**](https://tidbcloud.com/console/clusters) 页面中,选择你的 TiDB Serverless 集群,进入集群的 **Overview** 页面。 - class Meta: - database = db - table_name = "player" +2. 点击右上角的 **Connect** 按钮,将会弹出连接对话框。 +3. 确认对话框中的配置和你的运行环境一致。 -def random_player(amount: int) -> List[Player]: - players = [] - for _ in range(amount): - players.append(Player(id=uuid.uuid4(), coins=10000, goods=10000)) + - **Endpoint Type** 为 `Public`。 + - **Connect With** 选择 `General`。 + - **Operating System** 为你的运行环境。 - return players + > **Tip:** + > + > 如果你在 Windows Subsystem for Linux (WSL) 中运行,请切换为对应的 Linux 发行版。 +4. 如果你还没有设置密码,点击 **Create password** 生成一个随机密码。 -def simple_example() -> None: - # create a player, who has a coin and a goods. - Player.create(id="test", coins=1, goods=1) + > **Tip:** + > + > 如果你之前已经生成过密码,可以直接使用原密码,或点击 **Reset password** 重新生成密码。 - # get this player, and print it. - test_player = Player.select().where(Player.id == "test").get() - print(f'id:{test_player.id}, coins:{test_player.coins}, goods:{test_player.goods}') +5. 运行以下命令,将 `.env.example` 复制并重命名为 `.env`: - # create players with bulk inserts. - # insert 1919 players totally, with 114 players per batch. - # each player has a random UUID - player_list = random_player(1919) - Player.bulk_create(player_list, 114) + ```bash + cp .env.example .env + ``` - # print the number of players - count = Player.select().count() - print(f'number of players: {count}') - - # print 3 players. - three_players = Player.select().limit(3) - for player in three_players: - print(f'id:{player.id}, coins:{player.coins}, goods:{player.goods}') +6. 复制并粘贴对应连接字符串至 `.env` 中。示例结果如下: + ```dotenv + TIDB_HOST='{host}' # e.g. gateway01.ap-northeast-1.prod.aws.tidbcloud.com + TIDB_PORT='4000' + TIDB_USER='{user}' # e.g. xxxxxx.root + TIDB_PASSWORD='{password}' + TIDB_DB_NAME='test' + CA_PATH='{ssl_ca}' # e.g. /etc/ssl/certs/ca-certificates.crt (Debian / Ubuntu / Arch) + ``` -def trade_check(sell_id: str, buy_id: str, amount: int, price: int) -> bool: - sell_goods = Player.select(Player.goods).where(Player.id == sell_id).get().goods - if sell_goods < amount: - print(f'sell player {sell_id} goods not enough') - return False + 注意替换 `{}` 中的占位符为连接对话框中获得的值。 - buy_coins = Player.select(Player.coins).where(Player.id == buy_id).get().coins - if buy_coins < price: - print(f'buy player {buy_id} coins not enough') - return False +7. 保存 `.env` 文件。 - return True +
+
-def trade(sell_id: str, buy_id: str, amount: int, price: int) -> None: - with db.atomic() as txn: - try: - if trade_check(sell_id, buy_id, amount, price) is False: - txn.rollback() - return +1. 在 TiDB Cloud 的 [**Clusters**](https://tidbcloud.com/console/clusters) 页面中,选择你的 TiDB Dedicated 集群,进入集群的 **Overview** 页面。 - # deduct the goods of seller, and raise his/her the coins - Player.update(goods=Player.goods - amount, coins=Player.coins + price).where(Player.id == sell_id).execute() - # deduct the coins of buyer, and raise his/her the goods - Player.update(goods=Player.goods + amount, coins=Player.coins - price).where(Player.id == buy_id).execute() +2. 点击右上角的 **Connect** 按钮,将会出现连接对话框。 - except Exception as err: - txn.rollback() - print(f'something went wrong: {err}') - else: - txn.commit() - print("trade success") +3. 在对话框中点击 **Allow Access from Anywhere**,然后点击 **Download TiDB cluster CA** 下载 TiDB Cloud 提供的 CA 证书。 + 更多配置细节,可参考 [TiDB Dedicated 标准连接教程(英文)](https://docs.pingcap.com/tidbcloud/connect-via-standard-connection)。 -def trade_example() -> None: - # create two players - # player 1: id is "1", has only 100 coins. - # player 2: id is "2", has 114514 coins, and 20 goods. - Player.create(id="1", coins=100, goods=0) - Player.create(id="2", coins=114514, goods=20) +4. 运行以下命令,将 `.env.example` 复制并重命名为 `.env`: - # player 1 wants to buy 10 goods from player 2. - # it will cost 500 coins, but player 1 cannot afford it. - # so this trade will fail, and nobody will lose their coins or goods - trade(sell_id="2", buy_id="1", amount=10, price=500) + ```bash + cp .env.example .env + ``` - # then player 1 has to reduce the incoming quantity to 2. - # this trade will be successful - trade(sell_id="2", buy_id="1", amount=2, price=100) +5. 复制并粘贴对应的连接字符串至 `.env` 中。示例结果如下: - # let's take a look for player 1 and player 2 currently - after_trade_players = Player.select().where(Player.id.in_(["1", "2"])) - for player in after_trade_players: - print(f'id:{player.id}, coins:{player.coins}, goods:{player.goods}') + ```dotenv + TIDB_HOST='{host}' # e.g. tidb.xxxx.clusters.tidb-cloud.com + TIDB_PORT='4000' + TIDB_USER='{user}' # e.g. root + TIDB_PASSWORD='{password}' + TIDB_DB_NAME='test' + CA_PATH='{your-downloaded-ca-path}' + ``` + 注意替换 `{}` 中的占位符为连接对话框中获得的值,并配置前面步骤中下载好的证书路径。 -db.connect() +6. 保存 `.env` 文件。 -# recreate the player table -db.drop_tables([Player]) -db.create_tables([Player]) +
-simple_example() -trade_example() -``` +
-相较于直接使用 Driver,peewee 屏蔽了创建数据库连接时,不同数据库差异的细节。peewee 还封装了大量的操作,如会话管理、基本对象的 CRUD 等,极大地简化了代码量。 +1. 运行以下命令,将 `.env.example` 复制并重命名为 `.env`: -`Player` 类为数据库表在程序内的映射。`Player` 的每个属性都对应着 `player` 表的一个字段。peewee 使用 `Player` 类为了给 peewee 提供更多的信息,使用了形如以上示例中的 `id = CharField(max_length=36, primary_key=True)` 的类型定义,用来指示字段类型和其附加属性。`id = CharField(max_length=36, primary_key=True)` 表示 `id` 字段为 `CharField` 类型,对应数据库类型为 `VARCHAR`,长度为 `36`,且为主键。 + ```bash + cp .env.example .env + ``` -关于 peewee 的更多使用方法,你可以参考 [peewee 官网](http://docs.peewee-orm.com/en/latest/)。 +2. 复制并粘贴对应 TiDB 的连接字符串至 `.env` 中。示例结果如下: -## 第 3 步:运行代码 + ```dotenv + TIDB_HOST='{host}' + TIDB_PORT='4000' + TIDB_USER='root' + TIDB_PASSWORD='{password}' + TIDB_DB_NAME='test' + ``` -本节将逐步介绍代码的运行方法。 + 注意替换 `{}` 中的占位符为你的 TiDB 对应的值,并删除 `CA_PATH` 这行。如果你在本机运行 TiDB,默认 Host 地址为 `127.0.0.1`,密码为空。 -### 第 3 步第 1 部分:表初始化 +3. 保存 `.env` 文件。 -本示例需手动初始化表,若你使用本地集群,可直接运行: +
- + -
+### 第 4 步:运行代码并查看结果 -```shell -mysql --host 127.0.0.1 --port 4000 -u root < player_init.sql -``` +1. 运行下述命令,执行示例代码: -
+ ```bash + python peewee_example.py + ``` -
+2. 查看 [`Expected-Output.txt`](https://github.com/tidb-samples/tidb-python-peewee-quickstart/blob/main/Expected-Output.txt),并与你的程序输出进行比较。结果近似即为连接成功。 -```shell -mycli --host 127.0.0.1 --port 4000 -u root --no-warn < player_init.sql -``` +## 示例代码片段 -
+你可参考以下关键代码片段,完成自己的应用开发。 -
+完整代码及其运行方式,见代码仓库 [tidb-samples/tidb-python-peewee-quickstart](https://github.com/tidb-samples/tidb-python-peewee-quickstart)。 -若不使用本地集群,或未安装命令行客户端,请用喜欢的方式(如 Navicat、DBeaver 等 GUI 工具)直接登录集群,并运行 `player_init.sql` 文件内的 SQL 语句。 +### 连接到 TiDB -### 第 3 步第 2 部分:TiDB Cloud 更改参数 +```python +from peewee import MySQLDatabase + +def get_db_engine(): + config = Config() + connect_params = {} + if ${ca_path}: + connect_params = { + "ssl_verify_cert": True, + "ssl_verify_identity": True, + "ssl_ca": ${ca_path}, + } + return MySQLDatabase( + ${tidb_db_name}, + host=${tidb_host}, + port=${tidb_port}, + user=${tidb_user}, + password=${tidb_password}, + **connect_params, + ) +``` -若你使用了 TiDB Serverless 集群,此处需使用系统本地的 CA 证书,并将证书路径记为 `` 以供后续指代。你可以参考 [Where is the CA root path on my system?](https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters#where-is-the-ca-root-path-on-my-system) 文档获取你所使用的操作系统的 CA 证书位置。 +在使用该函数时,你需要将 `${tidb_host}`、`${tidb_port}`、`${tidb_user}`、`${tidb_password}`、`${tidb_db_name}` 以及 `${ca_path}` 替换为你的 TiDB 集群的实际值。 -若你使用 TiDB Serverless 集群,更改 `peewee_example.py` 内 `connect` 函数的入参: +### 声明数据对象 ```python -db = connect('mysql://root:@127.0.0.1:4000/test') +from peewee import Model, CharField, IntegerField + +db = get_db_engine() + +class BaseModel(Model): + class Meta: + database = db + +class Player(BaseModel): + name = CharField(max_length=32, unique=True) + coins = IntegerField(default=0) + goods = IntegerField(default=0) + + class Meta: + table_name = "players" ``` -若你设定的密码为 `123456`,而且从 TiDB Serverless 集群面板中得到的连接信息为: +更多信息参考 [peewee 模型与字段](https://docs.peewee-orm.com/en/latest/peewee/models.html)。 -- Endpoint: `xxx.tidbcloud.com` -- Port: `4000` -- User: `2aEp24QWEDLqRFs.root` +### 插入数据 -那么此处应将 `connect` 更改为: +```python +# 插入单个对象 +Player.create(name="test", coins=100, goods=100) + +# 插入多个对象 +Player.insert_many( + [ + {"name": "test1", "coins": 100, "goods": 100}, + {"name": "test2", "coins": 100, "goods": 100}, + ] +).execute() +``` -- peewee 将 PyMySQL 作为 Driver 时: +更多信息参考[插入数据](/develop/dev-guide-insert-data.md)。 - ```python - db = connect('mysql://2aEp24QWEDLqRFs.root:123456@xxx.tidbcloud.com:4000/test', - ssl_verify_cert=True, ssl_ca="") - ``` +### 查询数据 -- peewee 将 mysqlclient 作为 Driver 时: +```python +# 查询所有对象 +players = Player.select() - ```python - db = connect('mysql://2aEp24QWEDLqRFs.root:123456@xxx.tidbcloud.com:4000/test', - ssl_mode="VERIFY_IDENTITY", ssl={"ca": ""}) - ``` +# 查询单个对象 +player = Player.get(Player.name == "test") -由于 peewee 会将参数透传至 Driver 中,使用 peewee 时请注意 Driver 的使用类型。 +# 查询多个对象 +players = Player.select().where(Player.coins == 100) +``` -### 第 3 步第 3 部分:运行 +更多信息参考[查询数据](/develop/dev-guide-get-data-from-single-table.md)。 -运行前请先安装依赖: +### 更新数据 -```bash -pip3 install -r requirement.txt +```python +# 更新单个对象 +player = Player.get(Player.name == "test") +player.coins = 200 +player.save() + +# 批量更新多个对象 +Player.update(coins=200).where(Player.coins == 100).execute() ``` -当以后需要多次运行脚本时,请在每次运行前先依照[表初始化](#第-3-步第-1-部分表初始化)一节再次进行表初始化。 +更多信息参考[更新数据](/develop/dev-guide-update-data.md)。 -```bash -python3 peewee_example.py +### 删除数据 + +```python +# 删除单个对象 +player = Player.get(Player.name == "test") +player.delete_instance() + +# 批量删除多个对象 +Player.delete().where(Player.coins == 100).execute() ``` -## 第 4 步:预期输出 +更多信息参考[删除数据](/develop/dev-guide-delete-data.md)。 + +## 下一步 + +- 关于 peewee 的更多使用方法,可以参考 [peewee 官方文档](https://docs.peewee-orm.com/)。 +- 你可以继续阅读开发者文档,以获取更多关于 TiDB 应用开发的最佳实践。例如:[插入数据](/develop/dev-guide-insert-data.md)、[更新数据](/develop/dev-guide-update-data.md)、[删除数据](/develop/dev-guide-delete-data.md)、[单表读取](/develop/dev-guide-get-data-from-single-table.md)、[事务](/develop/dev-guide-transaction-overview.md)、[SQL 性能优化](/develop/dev-guide-optimize-sql-overview.md)等。 +- 如果你更倾向于参与课程进行学习,我们也提供专业的 [TiDB 开发者课程](https://cn.pingcap.com/courses-catalog/back-end-developer/?utm_source=docs-cn-dev-guide)支持,并在考试后提供相应的[资格认证](https://learn.pingcap.com/learner/certification-center)。 + +## 需要帮助? -[peewee 预期输出](https://github.com/pingcap-inc/tidb-example-python/blob/main/Expected-Output.md#peewee) +如果在开发的过程中遇到问题,可以在 [AskTUG](https://asktug.com/?utm_source=docs-cn-dev-guide) 上进行提问,寻求帮助。 \ No newline at end of file