<!-- markdownlint-disable MD033 MD036 MD041 -->
<p align="center">
<a href="https://v2.nonebot.dev/"><img src="https://v2.nonebot.dev/logo.png" width="200" height="200" alt="nonebot"></a>
</p>
<div align="center">
nonebot-plugin-sqlalchemy
============
_✨ NoneBot SQLAlchemy 封装插件 ✨_
</div>
<p align="center">
<a href="https://raw.githubusercontent.com/ssttkkl/nonebot-plugin-sqlalchemy/master/LICENSE">
<img src="https://img.shields.io/github/license/ssttkkl/nonebot-plugin-sqlalchemy.svg" alt="license">
</a>
<a href="https://pypi.python.org/pypi/nonebot-plugin-sqlalchemy">
<img src="https://img.shields.io/pypi/v/nonebot-plugin-sqlalchemy.svg" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="python">
</p>
为插件封装SQLAlchemy数据库访问,一个插件使用一个数据库。
对于数据存储较简单的场景,推荐使用[he0119/nonebot-plugin-datastore](https://github.com/he0119/nonebot-plugin-datastore)
## Get Started
### 1、定义data_source
```python
from nonebot import get_driver, require
# 注意必须先require再import
require("nonebot_plugin_sqlalchemy")
from nonebot_plugin_sqlalchemy import DataSource
# 必须使用支持asyncio的驱动器
db_conn_url = "postgresql+asyncpg://username:password@localhost:5432/database"
data_source = DataSource(get_driver(), db_conn_url)
```
### 2、定义映射
```python
from sqlalchemy.orm import mapped_column
@data_source.registry.mapped
class UserOrm:
__tablename__ = 'users'
id: int = mapped_column(primary_key=True, autoincrement=True)
username: str
password: str
nickname: str
```
### 3、在Matcher中使用
```python
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.internal.matcher import Matcher
from sqlalchemy import select
login_matcher = on_command("login")
@login_matcher.handle()
async def handler(event: MessageEvent, matcher: Matcher):
username, password = event.get_plaintext().split(" ")
session = data_source.session()
stmt = select(UserOrm).where(UserOrm.username == username, UserOrm.password == password)
result = await session.execute(stmt)
user = result.scalar_one_or_none()
if user is not None:
await matcher.send(f"Hello, {user.nickname}")
```
通过`data_source.session()`获取AsyncSession对象,此处获取的session实际上是async_scoped_session。
在Matcher的一次执行过程中,多次调用`data_source.session()`获得的是同一个session,并且会在Matcher执行完毕后自动关闭。也就是说我们可以像下面这样使用:
```python
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.internal.matcher import Matcher
from sqlalchemy import select
from typing import Optional
async def login(username: str, password: str) -> Optional[User]:
session = data_source.session()
stmt = select(UserOrm).where(UserOrm.username == username, UserOrm.password == password)
result = await session.execute(stmt)
user = result.scalar_one_or_none()
return user
login_matcher = on_command("login")
@login_matcher.handle()
async def handler(event: MessageEvent, matcher: Matcher):
username, password = event.get_plaintext().split(" ")
user = await login(username, password)
if user is not None:
await matcher.send(f"Hello, {user.nickname}")
```
参考:https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html#using-asyncio-scoped-session
注意:务必保证一次Matcher执行过程不会在不同的Task中调用`data_source.session()`获取session(即不要使用`create_task()`或`ensure_future()`创建Task),否则可能出现错误。若有这样的需求,请参考下文的方法手动创建并管理session。
### 4、在Matcher之外使用
在Matcher之外(如on_bot_connect等钩子函数中,或者是APScheduler的定时任务中)则必须通过`AsyncSession(data_source.engine)`创建session。
```python
async def do_something():
async with AsyncSession(data_source.engine) as session:
# ...
```
Raw data
{
"_id": null,
"home_page": "https://github.com/ssttkkl/nonebot-plugin-sqlalchemy",
"name": "nonebot-plugin-sqlalchemy",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "ssttkkl",
"author_email": "huang.wen.long@hotmail.com",
"download_url": "https://files.pythonhosted.org/packages/d7/22/db73b2b63cbc11ba94802e7ba49776358bafe213dff06fde86810e0b6149/nonebot_plugin_sqlalchemy-0.2.2.tar.gz",
"platform": null,
"description": "<!-- markdownlint-disable MD033 MD036 MD041 -->\n\n<p align=\"center\">\n <a href=\"https://v2.nonebot.dev/\"><img src=\"https://v2.nonebot.dev/logo.png\" width=\"200\" height=\"200\" alt=\"nonebot\"></a>\n</p>\n\n<div align=\"center\">\n\nnonebot-plugin-sqlalchemy\n============\n\n_\u2728 NoneBot SQLAlchemy \u5c01\u88c5\u63d2\u4ef6 \u2728_\n\n</div>\n\n<p align=\"center\">\n <a href=\"https://raw.githubusercontent.com/ssttkkl/nonebot-plugin-sqlalchemy/master/LICENSE\">\n <img src=\"https://img.shields.io/github/license/ssttkkl/nonebot-plugin-sqlalchemy.svg\" alt=\"license\">\n </a>\n <a href=\"https://pypi.python.org/pypi/nonebot-plugin-sqlalchemy\">\n <img src=\"https://img.shields.io/pypi/v/nonebot-plugin-sqlalchemy.svg\" alt=\"pypi\">\n </a>\n <img src=\"https://img.shields.io/badge/python-3.9+-blue.svg\" alt=\"python\">\n</p>\n\n\u4e3a\u63d2\u4ef6\u5c01\u88c5SQLAlchemy\u6570\u636e\u5e93\u8bbf\u95ee\uff0c\u4e00\u4e2a\u63d2\u4ef6\u4f7f\u7528\u4e00\u4e2a\u6570\u636e\u5e93\u3002\n\n\u5bf9\u4e8e\u6570\u636e\u5b58\u50a8\u8f83\u7b80\u5355\u7684\u573a\u666f\uff0c\u63a8\u8350\u4f7f\u7528[he0119/nonebot-plugin-datastore](https://github.com/he0119/nonebot-plugin-datastore)\n\n## Get Started\n\n### 1\u3001\u5b9a\u4e49data_source\n\n```python\nfrom nonebot import get_driver, require\n\n# \u6ce8\u610f\u5fc5\u987b\u5148require\u518dimport\nrequire(\"nonebot_plugin_sqlalchemy\")\nfrom nonebot_plugin_sqlalchemy import DataSource\n\n# \u5fc5\u987b\u4f7f\u7528\u652f\u6301asyncio\u7684\u9a71\u52a8\u5668\ndb_conn_url = \"postgresql+asyncpg://username:password@localhost:5432/database\"\ndata_source = DataSource(get_driver(), db_conn_url)\n```\n\n### 2\u3001\u5b9a\u4e49\u6620\u5c04\n```python\nfrom sqlalchemy.orm import mapped_column\n\n@data_source.registry.mapped\nclass UserOrm:\n __tablename__ = 'users'\n\n id: int = mapped_column(primary_key=True, autoincrement=True)\n username: str\n password: str\n nickname: str\n```\n\n### 3\u3001\u5728Matcher\u4e2d\u4f7f\u7528\n\n```python\nfrom nonebot import on_command\nfrom nonebot.adapters.onebot.v11 import MessageEvent\nfrom nonebot.internal.matcher import Matcher\nfrom sqlalchemy import select\n\nlogin_matcher = on_command(\"login\")\n\n\n@login_matcher.handle()\nasync def handler(event: MessageEvent, matcher: Matcher):\n username, password = event.get_plaintext().split(\" \")\n\n session = data_source.session()\n \n stmt = select(UserOrm).where(UserOrm.username == username, UserOrm.password == password)\n result = await session.execute(stmt)\n user = result.scalar_one_or_none()\n\n if user is not None:\n await matcher.send(f\"Hello, {user.nickname}\")\n```\n\n\u901a\u8fc7`data_source.session()`\u83b7\u53d6AsyncSession\u5bf9\u8c61\uff0c\u6b64\u5904\u83b7\u53d6\u7684session\u5b9e\u9645\u4e0a\u662fasync_scoped_session\u3002\n\n\u5728Matcher\u7684\u4e00\u6b21\u6267\u884c\u8fc7\u7a0b\u4e2d\uff0c\u591a\u6b21\u8c03\u7528`data_source.session()`\u83b7\u5f97\u7684\u662f\u540c\u4e00\u4e2asession\uff0c\u5e76\u4e14\u4f1a\u5728Matcher\u6267\u884c\u5b8c\u6bd5\u540e\u81ea\u52a8\u5173\u95ed\u3002\u4e5f\u5c31\u662f\u8bf4\u6211\u4eec\u53ef\u4ee5\u50cf\u4e0b\u9762\u8fd9\u6837\u4f7f\u7528\uff1a\n\n```python\nfrom nonebot import on_command\nfrom nonebot.adapters.onebot.v11 import MessageEvent\nfrom nonebot.internal.matcher import Matcher\nfrom sqlalchemy import select\nfrom typing import Optional\n\nasync def login(username: str, password: str) -> Optional[User]:\n session = data_source.session()\n \n stmt = select(UserOrm).where(UserOrm.username == username, UserOrm.password == password)\n result = await session.execute(stmt)\n user = result.scalar_one_or_none()\n\n return user\n\n\nlogin_matcher = on_command(\"login\")\n\n\n@login_matcher.handle()\nasync def handler(event: MessageEvent, matcher: Matcher):\n username, password = event.get_plaintext().split(\" \")\n user = await login(username, password)\n if user is not None:\n await matcher.send(f\"Hello, {user.nickname}\")\n```\n\n\u53c2\u8003\uff1ahttps://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html#using-asyncio-scoped-session\n\n\u6ce8\u610f\uff1a\u52a1\u5fc5\u4fdd\u8bc1\u4e00\u6b21Matcher\u6267\u884c\u8fc7\u7a0b\u4e0d\u4f1a\u5728\u4e0d\u540c\u7684Task\u4e2d\u8c03\u7528`data_source.session()`\u83b7\u53d6session\uff08\u5373\u4e0d\u8981\u4f7f\u7528`create_task()`\u6216`ensure_future()`\u521b\u5efaTask\uff09\uff0c\u5426\u5219\u53ef\u80fd\u51fa\u73b0\u9519\u8bef\u3002\u82e5\u6709\u8fd9\u6837\u7684\u9700\u6c42\uff0c\u8bf7\u53c2\u8003\u4e0b\u6587\u7684\u65b9\u6cd5\u624b\u52a8\u521b\u5efa\u5e76\u7ba1\u7406session\u3002\n\n\n### 4\u3001\u5728Matcher\u4e4b\u5916\u4f7f\u7528\n\n\u5728Matcher\u4e4b\u5916\uff08\u5982on_bot_connect\u7b49\u94a9\u5b50\u51fd\u6570\u4e2d\uff0c\u6216\u8005\u662fAPScheduler\u7684\u5b9a\u65f6\u4efb\u52a1\u4e2d\uff09\u5219\u5fc5\u987b\u901a\u8fc7`AsyncSession(data_source.engine)`\u521b\u5efasession\u3002\n\n```python\nasync def do_something():\n async with AsyncSession(data_source.engine) as session:\n # ...\n```\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "",
"version": "0.2.2",
"project_urls": {
"Homepage": "https://github.com/ssttkkl/nonebot-plugin-sqlalchemy",
"Repository": "https://github.com/ssttkkl/nonebot-plugin-sqlalchemy"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "f297d7b87eee47f6c9beabcea61991c0c03134f947010729d204c71e7b0177da",
"md5": "d773b78f06463d4bd4dc632b13b257ed",
"sha256": "4ec310daafdea259cd0b81171313a8a3fc23d90edf18f75f298d14c58c7d4327"
},
"downloads": -1,
"filename": "nonebot_plugin_sqlalchemy-0.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d773b78f06463d4bd4dc632b13b257ed",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9,<4.0",
"size": 5310,
"upload_time": "2023-07-20T03:51:36",
"upload_time_iso_8601": "2023-07-20T03:51:36.974013Z",
"url": "https://files.pythonhosted.org/packages/f2/97/d7b87eee47f6c9beabcea61991c0c03134f947010729d204c71e7b0177da/nonebot_plugin_sqlalchemy-0.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d722db73b2b63cbc11ba94802e7ba49776358bafe213dff06fde86810e0b6149",
"md5": "1eb77247f7b762308ceea2252af7d188",
"sha256": "2b3f76768d9c08f8715d2d892499d3ca36a2b7a37eabe2eff06617551b04cf06"
},
"downloads": -1,
"filename": "nonebot_plugin_sqlalchemy-0.2.2.tar.gz",
"has_sig": false,
"md5_digest": "1eb77247f7b762308ceea2252af7d188",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9,<4.0",
"size": 4688,
"upload_time": "2023-07-20T03:51:38",
"upload_time_iso_8601": "2023-07-20T03:51:38.342309Z",
"url": "https://files.pythonhosted.org/packages/d7/22/db73b2b63cbc11ba94802e7ba49776358bafe213dff06fde86810e0b6149/nonebot_plugin_sqlalchemy-0.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-07-20 03:51:38",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ssttkkl",
"github_project": "nonebot-plugin-sqlalchemy",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "nonebot-plugin-sqlalchemy"
}