<!-- markdownlint-disable MD033 MD041 -->
<p align="center">
<a href="https://nonebot.dev/"><img src="https://nonebot.dev/logo.png" width="200" height="200" alt="nonebot"></a>
</p>
<div align="center">
# NoneBot Plugin ORM
<!-- prettier-ignore-start -->
<!-- markdownlint-disable-next-line MD036 -->
_✨ NoneBot 数据库支持插件 ✨_
<!-- prettier-ignore-end -->
</div>
<p align="center">
<a href="https://raw.githubusercontent.com/nonebot/plugin-orm/master/LICENSE">
<img src="https://img.shields.io/github/license/nonebot/plugin-orm.svg" alt="license">
</a>
<a href="https://pypi.org/project/nonebot-plugin-orm/">
<img src="https://img.shields.io/pypi/v/nonebot-plugin-orm.svg" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
</p>
## 安装
```shell
pip install nonebot-plugin-orm
poetry add nonebot-plugin-orm
pdm add nonebot-plugin-orm
# 无需配置、开箱即用的默认依赖
pip install nonebot-plugin-orm[default]
# 特定数据库后端的依赖
pip install nonebot-plugin-orm[mysql]
pip install nonebot-plugin-orm[postgresql]
pip install nonebot-plugin-orm[sqlite]
# 特定数据库驱动的依赖
pip install nonebot-plugin-orm[asyncmy]
pip install nonebot-plugin-orm[aiomysql]
pip install nonebot-plugin-orm[psycopg]
pip install nonebot-plugin-orm[asyncpg]
pip install nonebot-plugin-orm[aiosqlite]
```
## 使用方式
### ORM
#### Model 依赖注入
```python
from nonebot.adapters import Event
from nonebot.params import Depends
from nonebot import require, on_message
from sqlalchemy.orm import Mapped, mapped_column
require("nonebot_plugin_orm")
from nonebot_plugin_orm import Model, async_scoped_session
matcher = on_message()
def get_user_id(event: Event) -> str:
return event.get_user_id()
class User(Model):
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[str] = Depends(get_user_id)
@matcher.handle()
async def _(event: Event, sess: async_scoped_session, user: User | None):
if user:
await matcher.finish(f"Hello, {user.user_id}")
sess.add(User(user_id=get_user_id(event)))
await sess.commit()
await matcher.finish("Hello, new user!")
```
#### SQL 依赖注入
```python
from sqlalchemy import select
from nonebot.adapters import Event
from nonebot.params import Depends
from nonebot import require, on_message
from sqlalchemy.orm import Mapped, mapped_column
require("nonebot_plugin_orm")
from nonebot_plugin_orm import Model, SQLDepends, async_scoped_session
matcher = on_message()
def get_session_id(event: Event) -> str:
return event.get_session_id()
class Session(Model):
id: Mapped[int] = mapped_column(primary_key=True)
session_id: Mapped[str]
@matcher.handle()
async def _(
event: Event,
sess: async_scoped_session,
session: Session
| None = SQLDepends(
select(Session).where(Session.session_id == Depends(get_session_id))
),
):
if session:
await matcher.finish(f"Hello, {session.session_id}")
sess.add(Session(session_id=get_session_id(event)))
await sess.commit()
await matcher.finish("Hello, new user!")
```
### CLI
依赖 [NB CLI](https://github.com/nonebot/nb-cli)
```properties
$ nb orm
Usage: nb orm [OPTIONS] COMMAND [ARGS]...
Options:
-c, --config FILE 可选的配置文件;默认为 ALEMBIC_CONFIG 环境变量的值,或者 "alembic.ini"(如果存在)
-n, --name TEXT .ini 文件中用于 Alembic 配置的小节的名称 [default: alembic]
-x TEXT 自定义 env.py 脚本使用的其他参数,例如:-x setting1=somesetting -x
setting2=somesetting
-q, --quite 不要输出日志到标准输出
--help Show this message and exit.
Commands:
branches 显示所有的分支。
check 检查数据库是否与模型定义一致。
current 显示当前的迁移。
downgrade 回退到先前版本。
edit 使用 $EDITOR 编辑迁移脚本。
ensure_version 创建版本表。
heads 显示所有的分支头。
history 显示迁移的历史。
init 初始化脚本目录。
list_templates 列出所有可用的模板。
merge 合并多个迁移。创建一个新的迁移脚本。
revision 创建一个新迁移脚本。
show 显示迁移的信息。
stamp 将数据库标记为特定的迁移版本,不运行任何迁移。
upgrade 升级到较新版本。
```
## 配置项
### sqlalchemy_database_url
默认数据库连接 URL。
参见:[Engine Configuration — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)
```properties
SQLALCHEMY_DATABASE_URL=sqlite+aiosqlite://
```
### sqlalchemy_binds
bind keys 到 `AsyncEngine` 选项的映射。值可以是数据库连接 URL、`AsyncEngine` 选项字典或者 `AsyncEngine` 实例。
```properties
SQLALCHEMY_BINDS='{
"": "sqlite+aiosqlite://",
"nonebot_plugin_user": {
"url": "postgresql+asyncpg://scott:tiger@localhost/mydatabase",
"echo": true
}
}'
```
### sqlalchemy_echo
所有 `AsyncEngine` 的 `echo` 和 `echo_pool` 选项的默认值。用于快速调试连接和 SQL 生成问题。
```properties
SQLALCHEMY_ECHO=true
```
### sqlalchemy_engine_options
所有 `AsyncEngine` 的默认选项字典。
参见:[Engine Configuration — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#engine-configuration)
```properties
SQLALCHEMY_ENGINE_OPTIONS='{
"pool_size": 5,
"max_overflow": 10,
"pool_timeout": 30,
"pool_recycle": 3600,
"echo": true
}'
```
### sqlalchemy_session_options
`AsyncSession` 的选项字典。
参见:[Session API — SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.__init__)
```properties
SQLALCHEMY_SESSION_OPTIONS='{
"autoflush": false,
"autobegin": true,
"expire_on_commit": true
}'
```
### alembic_config
配置文件路径或 `AlembicConfig` 实例。
```properties
ALEMBIC_CONFIG=alembic.ini
```
### alembic_script_location
脚本目录路径。
```properties
ALEMBIC_SCRIPT_LOCATION=migrations
```
### alembic_version_locations
迁移脚本目录路径或分支标签到迁移脚本目录路径的映射。
```properties
ALEMBIC_VERSION_LOCATIONS=migrations/versions
ALEMBIC_VERSION_LOCATIONS='{
"": "migrations/versions",
"nonebot_plugin_user": "src/nonebot_plugin_user/versions",
"nonebot_plugin_chatrecorder": "migrations/versions/nonebot_plugin_chatrecorder"
}'
```
### alembic_context
`MigrationContext` 的选项字典。
参见:[Runtime Objects — Alembic 1.12.0 documentation](https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure)
```properties
ALEMBIC_CONTEXT='{
"render_as_batch": true
}'
```
### alembic_startup_check
是否在启动时检查数据库与模型定义的一致性。
```properties
ALEMBIC_STARTUP_CHECK=true
```
Raw data
{
"_id": null,
"home_page": "https://github.com/nonebot/plugin-orm",
"name": "nonebot-plugin-orm",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "nonebot, orm, sqlalchemy",
"author": null,
"author_email": "yanyongyu <yyy@nonebot.dev>, ProgramRipper <programripper@foxmail.com>",
"download_url": "https://files.pythonhosted.org/packages/cb/21/68a9efa50444ead2f6ee5bc2de69acaafb2a705e1bea734cc4b1d5b5c459/nonebot_plugin_orm-0.7.6.tar.gz",
"platform": null,
"description": "<!-- markdownlint-disable MD033 MD041 -->\n<p align=\"center\">\n <a href=\"https://nonebot.dev/\"><img src=\"https://nonebot.dev/logo.png\" width=\"200\" height=\"200\" alt=\"nonebot\"></a>\n</p>\n\n<div align=\"center\">\n\n# NoneBot Plugin ORM\n\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable-next-line MD036 -->\n_\u2728 NoneBot \u6570\u636e\u5e93\u652f\u6301\u63d2\u4ef6 \u2728_\n<!-- prettier-ignore-end -->\n\n</div>\n\n<p align=\"center\">\n <a href=\"https://raw.githubusercontent.com/nonebot/plugin-orm/master/LICENSE\">\n <img src=\"https://img.shields.io/github/license/nonebot/plugin-orm.svg\" alt=\"license\">\n </a>\n <a href=\"https://pypi.org/project/nonebot-plugin-orm/\">\n <img src=\"https://img.shields.io/pypi/v/nonebot-plugin-orm.svg\" alt=\"pypi\">\n </a>\n <img src=\"https://img.shields.io/badge/python-3.8+-blue.svg\" alt=\"python\">\n</p>\n\n## \u5b89\u88c5\n\n```shell\npip install nonebot-plugin-orm\npoetry add nonebot-plugin-orm\npdm add nonebot-plugin-orm\n\n# \u65e0\u9700\u914d\u7f6e\u3001\u5f00\u7bb1\u5373\u7528\u7684\u9ed8\u8ba4\u4f9d\u8d56\npip install nonebot-plugin-orm[default]\n\n# \u7279\u5b9a\u6570\u636e\u5e93\u540e\u7aef\u7684\u4f9d\u8d56\npip install nonebot-plugin-orm[mysql]\npip install nonebot-plugin-orm[postgresql]\npip install nonebot-plugin-orm[sqlite]\n\n# \u7279\u5b9a\u6570\u636e\u5e93\u9a71\u52a8\u7684\u4f9d\u8d56\npip install nonebot-plugin-orm[asyncmy]\npip install nonebot-plugin-orm[aiomysql]\npip install nonebot-plugin-orm[psycopg]\npip install nonebot-plugin-orm[asyncpg]\npip install nonebot-plugin-orm[aiosqlite]\n```\n\n## \u4f7f\u7528\u65b9\u5f0f\n\n### ORM\n\n#### Model \u4f9d\u8d56\u6ce8\u5165\n\n```python\nfrom nonebot.adapters import Event\nfrom nonebot.params import Depends\nfrom nonebot import require, on_message\nfrom sqlalchemy.orm import Mapped, mapped_column\n\nrequire(\"nonebot_plugin_orm\")\nfrom nonebot_plugin_orm import Model, async_scoped_session\n\nmatcher = on_message()\n\n\ndef get_user_id(event: Event) -> str:\n return event.get_user_id()\n\n\nclass User(Model):\n id: Mapped[int] = mapped_column(primary_key=True)\n user_id: Mapped[str] = Depends(get_user_id)\n\n\n@matcher.handle()\nasync def _(event: Event, sess: async_scoped_session, user: User | None):\n if user:\n await matcher.finish(f\"Hello, {user.user_id}\")\n\n sess.add(User(user_id=get_user_id(event)))\n await sess.commit()\n await matcher.finish(\"Hello, new user!\")\n```\n\n#### SQL \u4f9d\u8d56\u6ce8\u5165\n\n```python\nfrom sqlalchemy import select\nfrom nonebot.adapters import Event\nfrom nonebot.params import Depends\nfrom nonebot import require, on_message\nfrom sqlalchemy.orm import Mapped, mapped_column\n\nrequire(\"nonebot_plugin_orm\")\nfrom nonebot_plugin_orm import Model, SQLDepends, async_scoped_session\n\nmatcher = on_message()\n\n\ndef get_session_id(event: Event) -> str:\n return event.get_session_id()\n\n\nclass Session(Model):\n id: Mapped[int] = mapped_column(primary_key=True)\n session_id: Mapped[str]\n\n\n@matcher.handle()\nasync def _(\n event: Event,\n sess: async_scoped_session,\n session: Session\n | None = SQLDepends(\n select(Session).where(Session.session_id == Depends(get_session_id))\n ),\n):\n if session:\n await matcher.finish(f\"Hello, {session.session_id}\")\n\n sess.add(Session(session_id=get_session_id(event)))\n await sess.commit()\n await matcher.finish(\"Hello, new user!\")\n\n```\n\n### CLI\n\n\u4f9d\u8d56 [NB CLI](https://github.com/nonebot/nb-cli)\n\n```properties\n$ nb orm\nUsage: nb orm [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n -c, --config FILE \u53ef\u9009\u7684\u914d\u7f6e\u6587\u4ef6\uff1b\u9ed8\u8ba4\u4e3a ALEMBIC_CONFIG \u73af\u5883\u53d8\u91cf\u7684\u503c\uff0c\u6216\u8005 \"alembic.ini\"\uff08\u5982\u679c\u5b58\u5728\uff09\n -n, --name TEXT .ini \u6587\u4ef6\u4e2d\u7528\u4e8e Alembic \u914d\u7f6e\u7684\u5c0f\u8282\u7684\u540d\u79f0 [default: alembic]\n -x TEXT \u81ea\u5b9a\u4e49 env.py \u811a\u672c\u4f7f\u7528\u7684\u5176\u4ed6\u53c2\u6570\uff0c\u4f8b\u5982\uff1a-x setting1=somesetting -x\n setting2=somesetting\n -q, --quite \u4e0d\u8981\u8f93\u51fa\u65e5\u5fd7\u5230\u6807\u51c6\u8f93\u51fa\n --help Show this message and exit.\n\nCommands:\n branches \u663e\u793a\u6240\u6709\u7684\u5206\u652f\u3002\n check \u68c0\u67e5\u6570\u636e\u5e93\u662f\u5426\u4e0e\u6a21\u578b\u5b9a\u4e49\u4e00\u81f4\u3002\n current \u663e\u793a\u5f53\u524d\u7684\u8fc1\u79fb\u3002\n downgrade \u56de\u9000\u5230\u5148\u524d\u7248\u672c\u3002\n edit \u4f7f\u7528 $EDITOR \u7f16\u8f91\u8fc1\u79fb\u811a\u672c\u3002\n ensure_version \u521b\u5efa\u7248\u672c\u8868\u3002\n heads \u663e\u793a\u6240\u6709\u7684\u5206\u652f\u5934\u3002\n history \u663e\u793a\u8fc1\u79fb\u7684\u5386\u53f2\u3002\n init \u521d\u59cb\u5316\u811a\u672c\u76ee\u5f55\u3002\n list_templates \u5217\u51fa\u6240\u6709\u53ef\u7528\u7684\u6a21\u677f\u3002\n merge \u5408\u5e76\u591a\u4e2a\u8fc1\u79fb\u3002\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u8fc1\u79fb\u811a\u672c\u3002\n revision \u521b\u5efa\u4e00\u4e2a\u65b0\u8fc1\u79fb\u811a\u672c\u3002\n show \u663e\u793a\u8fc1\u79fb\u7684\u4fe1\u606f\u3002\n stamp \u5c06\u6570\u636e\u5e93\u6807\u8bb0\u4e3a\u7279\u5b9a\u7684\u8fc1\u79fb\u7248\u672c\uff0c\u4e0d\u8fd0\u884c\u4efb\u4f55\u8fc1\u79fb\u3002\n upgrade \u5347\u7ea7\u5230\u8f83\u65b0\u7248\u672c\u3002\n```\n\n## \u914d\u7f6e\u9879\n\n### sqlalchemy_database_url\n\n\u9ed8\u8ba4\u6570\u636e\u5e93\u8fde\u63a5 URL\u3002\n\u53c2\u89c1\uff1a[Engine Configuration \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)\n\n```properties\nSQLALCHEMY_DATABASE_URL=sqlite+aiosqlite://\n```\n\n### sqlalchemy_binds\n\nbind keys \u5230 `AsyncEngine` \u9009\u9879\u7684\u6620\u5c04\u3002\u503c\u53ef\u4ee5\u662f\u6570\u636e\u5e93\u8fde\u63a5 URL\u3001`AsyncEngine` \u9009\u9879\u5b57\u5178\u6216\u8005 `AsyncEngine` \u5b9e\u4f8b\u3002\n\n```properties\nSQLALCHEMY_BINDS='{\n \"\": \"sqlite+aiosqlite://\",\n \"nonebot_plugin_user\": {\n \"url\": \"postgresql+asyncpg://scott:tiger@localhost/mydatabase\",\n \"echo\": true\n }\n}'\n```\n\n### sqlalchemy_echo\n\n\u6240\u6709 `AsyncEngine` \u7684 `echo` \u548c `echo_pool` \u9009\u9879\u7684\u9ed8\u8ba4\u503c\u3002\u7528\u4e8e\u5feb\u901f\u8c03\u8bd5\u8fde\u63a5\u548c SQL \u751f\u6210\u95ee\u9898\u3002\n\n```properties\nSQLALCHEMY_ECHO=true\n```\n\n### sqlalchemy_engine_options\n\n\u6240\u6709 `AsyncEngine` \u7684\u9ed8\u8ba4\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Engine Configuration \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#engine-configuration)\n\n```properties\nSQLALCHEMY_ENGINE_OPTIONS='{\n \"pool_size\": 5,\n \"max_overflow\": 10,\n \"pool_timeout\": 30,\n \"pool_recycle\": 3600,\n \"echo\": true\n}'\n```\n\n### sqlalchemy_session_options\n\n`AsyncSession` \u7684\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Session API \u2014 SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.__init__)\n\n```properties\nSQLALCHEMY_SESSION_OPTIONS='{\n \"autoflush\": false,\n \"autobegin\": true,\n \"expire_on_commit\": true\n}'\n```\n\n### alembic_config\n\n\u914d\u7f6e\u6587\u4ef6\u8def\u5f84\u6216 `AlembicConfig` \u5b9e\u4f8b\u3002\n\n```properties\nALEMBIC_CONFIG=alembic.ini\n```\n\n### alembic_script_location\n\n\u811a\u672c\u76ee\u5f55\u8def\u5f84\u3002\n\n```properties\nALEMBIC_SCRIPT_LOCATION=migrations\n```\n\n### alembic_version_locations\n\n\u8fc1\u79fb\u811a\u672c\u76ee\u5f55\u8def\u5f84\u6216\u5206\u652f\u6807\u7b7e\u5230\u8fc1\u79fb\u811a\u672c\u76ee\u5f55\u8def\u5f84\u7684\u6620\u5c04\u3002\n\n```properties\nALEMBIC_VERSION_LOCATIONS=migrations/versions\n\nALEMBIC_VERSION_LOCATIONS='{\n \"\": \"migrations/versions\",\n \"nonebot_plugin_user\": \"src/nonebot_plugin_user/versions\",\n \"nonebot_plugin_chatrecorder\": \"migrations/versions/nonebot_plugin_chatrecorder\"\n}'\n```\n\n### alembic_context\n\n`MigrationContext` \u7684\u9009\u9879\u5b57\u5178\u3002\n\u53c2\u89c1\uff1a[Runtime Objects \u2014 Alembic 1.12.0 documentation](https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure)\n\n```properties\nALEMBIC_CONTEXT='{\n \"render_as_batch\": true\n}'\n```\n\n### alembic_startup_check\n\n\u662f\u5426\u5728\u542f\u52a8\u65f6\u68c0\u67e5\u6570\u636e\u5e93\u4e0e\u6a21\u578b\u5b9a\u4e49\u7684\u4e00\u81f4\u6027\u3002\n\n```properties\nALEMBIC_STARTUP_CHECK=true\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "SQLAlchemy ORM support for nonebot",
"version": "0.7.6",
"project_urls": {
"Documentation": "https://github.com/nonebot/plugin-orm",
"Homepage": "https://github.com/nonebot/plugin-orm",
"Repository": "https://github.com/nonebot/plugin-orm"
},
"split_keywords": [
"nonebot",
" orm",
" sqlalchemy"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "cac56ae05bf3a667231dbdbc923e9980611fdb4f144deb620b3b9a06bd1a54b6",
"md5": "cc1fd2fceccf4fea1cc8ae45ebb7e0e0",
"sha256": "6ce808d7e847eb7c1a738609b0e94bb612488650c5150ae1b2d67463034bb255"
},
"downloads": -1,
"filename": "nonebot_plugin_orm-0.7.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "cc1fd2fceccf4fea1cc8ae45ebb7e0e0",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 32884,
"upload_time": "2024-08-09T08:21:22",
"upload_time_iso_8601": "2024-08-09T08:21:22.116586Z",
"url": "https://files.pythonhosted.org/packages/ca/c5/6ae05bf3a667231dbdbc923e9980611fdb4f144deb620b3b9a06bd1a54b6/nonebot_plugin_orm-0.7.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "cb2168a9efa50444ead2f6ee5bc2de69acaafb2a705e1bea734cc4b1d5b5c459",
"md5": "07c8ed5707d5933b4b764a131eecf2a2",
"sha256": "3ae4ac362a8ea6e6467666f654287855a30447f8fa457af88f1760f155c5d68c"
},
"downloads": -1,
"filename": "nonebot_plugin_orm-0.7.6.tar.gz",
"has_sig": false,
"md5_digest": "07c8ed5707d5933b4b764a131eecf2a2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 27537,
"upload_time": "2024-08-09T08:21:23",
"upload_time_iso_8601": "2024-08-09T08:21:23.675249Z",
"url": "https://files.pythonhosted.org/packages/cb/21/68a9efa50444ead2f6ee5bc2de69acaafb2a705e1bea734cc4b1d5b5c459/nonebot_plugin_orm-0.7.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-09 08:21:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "nonebot",
"github_project": "plugin-orm",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "nonebot-plugin-orm"
}