# FunTable
[](https://badge.fury.io/py/funtable)
[](https://pypi.org/project/funtable/)
[](https://github.com/farfarfun/funtable/blob/main/LICENSE)
一个统一的表存储抽象库,为各种存储后端(包括 SQLite、TinyDB、SQLModel 和云存储快照)提供一致的接口。
## 特性
- **统一接口**: 跨不同存储后端的一致 API
- **多种存储类型**: 支持 KV(键值)和 KKV(键键值)存储模式
- **事务支持**: 内置事务管理,确保数据一致性
- **多种后端**: 支持 SQLite、TinyDB、SQLModel 和云存储
- **快照管理**: 自动备份和版本管理,支持云存储
- **线程安全**: 为并发访问场景而设计
## 安装
### 基础安装
```bash
pip install funtable
```
### 安装可选依赖
```bash
# 支持 SQLModel
pip install funtable[sqlmodel]
# 支持快照/云存储
pip install funtable[snapshot]
# 支持 TinyDB
pip install funtable[kv]
# 安装所有可选依赖
pip install funtable[sqlmodel,snapshot,kv]
```
## 快速开始
### KV(键值)存储
```python
from funtable.kv import SQLiteDB
# 初始化数据库
db = SQLiteDB("my_database.db")
# 创建 KV 表
db.create_kv_table("users")
table = db.get_table("users")
# 存储数据
table.set("user1", {"name": "Alice", "age": 30})
table.set("user2", {"name": "Bob", "age": 25})
# 检索数据
user = table.get("user1")
print(user) # {"name": "Alice", "age": 30}
# 列出所有键
keys = table.list_keys()
print(keys) # ["user1", "user2"]
# 获取所有数据
all_data = table.list_all()
print(all_data)
```
### KKV(键键值)存储
```python
from funtable.kv import SQLiteDB
# 初始化数据库
db = SQLiteDB("my_database.db")
# 创建 KKV 表
db.create_kkv_table("user_profiles")
table = db.get_table("user_profiles")
# 使用两级键存储数据
table.set("user1", "profile", {"bio": "Software Engineer"})
table.set("user1", "settings", {"theme": "dark", "notifications": True})
table.set("user2", "profile", {"bio": "Data Scientist"})
# 检索数据
profile = table.get("user1", "profile")
print(profile) # {"bio": "Software Engineer"}
# 列出主键
pkeys = table.list_pkeys()
print(pkeys) # ["user1", "user2"]
# 列出某个主键的次键
skeys = table.list_skeys("user1")
print(skeys) # ["profile", "settings"]
```
### 事务支持
```python
# 使用事务确保数据一致性
table.begin_transaction()
try:
table.set("user3", {"name": "Charlie", "age": 35})
table.set("user4", {"name": "David", "age": 40})
table.commit()
except Exception as e:
table.rollback()
print(f"事务失败: {e}")
```
### SQLModel 集成
```python
from funtable.sqlmodel import BaseModel
from sqlmodel import Field
from datetime import datetime
class User(BaseModel, table=True):
name: str = Field(description="用户名")
email: str = Field(description="用户邮箱")
age: int = Field(description="用户年龄")
# BaseModel 提供内置字段:
# - id: 自增主键
# - uid: 唯一标识符
# - gmt_create: 创建时间戳
# - gmt_modified: 修改时间戳
# 与 SQLModel session 一起使用
from sqlmodel import create_engine, Session
engine = create_engine("sqlite:///users.db")
with Session(engine) as session:
user = User(name="Alice", email="alice@example.com", age=30)
session.add(user)
session.commit()
# 查询方法
all_users = User.all(session)
user_by_id = User.by_id(1, session)
user_by_uid = User.by_uid("some-uid", session)
```
### 快照管理
```python
from funtable.snapshot import DriveSnapshot
from fundrive import SomeDriveImplementation
# 初始化驱动器和快照管理器
drive = SomeDriveImplementation()
snapshot = DriveSnapshot(
table_fid="my-table-id",
drive=drive,
num=7 # 保留 7 个版本
)
# 创建快照
snapshot.update("/path/to/data", partition="20231201")
# 下载最新快照
snapshot.download("/path/to/restore")
```
## 存储后端
### SQLite 后端
- **文件**: `funtable.kv.sqlite_table`
- **特性**: ACID 事务、SQL 查询、基于文件的存储
- **适用于**: 生产应用、复杂查询、数据完整性
### TinyDB 后端
- **文件**: `funtable.kv.tinydb_table`
- **特性**: 基于 JSON、轻量级、无外部依赖
- **适用于**: 小型应用、原型开发、简单数据结构
### SQLModel 集成
- **文件**: `funtable.sqlmodel.base`
- **特性**: ORM 功能、类型安全、Pydantic 集成
- **适用于**: 现代 Python 应用、API 开发、数据验证
### 云存储快照
- **文件**: `funtable.snapshot.core`
- **特性**: 自动备份、版本管理、云集成
- **适用于**: 数据备份、灾难恢复、分布式系统
## API 参考
### 核心接口
#### BaseKVTable
- `set(key: str, value: Dict) -> None`: 存储键值对
- `get(key: str) -> Optional[Dict]`: 通过键检索值
- `delete(key: str) -> bool`: 删除键值对
- `list_keys() -> List[str]`: 获取所有键
- `list_all() -> Dict[str, Dict]`: 获取所有键值对
- `begin_transaction()`, `commit()`, `rollback()`: 事务管理
#### BaseKKVTable
- `set(pkey: str, skey: str, value: Dict) -> None`: 使用两级键存储
- `get(pkey: str, skey: str) -> Optional[Dict]`: 通过两级键检索
- `delete(pkey: str, skey: str) -> bool`: 通过两级键删除
- `list_pkeys() -> List[str]`: 获取所有主键
- `list_skeys(pkey: str) -> List[str]`: 获取主键的次键
- `list_all() -> Dict[str, Dict[str, Dict]]`: 获取所有数据
- `batch_set(items: Dict) -> None`: 批量插入操作
- `batch_delete(items: List[tuple]) -> None`: 批量删除操作
#### BaseDB
- `create_kv_table(table_name: str) -> None`: 创建 KV 表
- `create_kkv_table(table_name: str) -> None`: 创建 KKV 表
- `get_table(table_name: str) -> Union[BaseKVTable, BaseKKVTable]`: 获取表实例
- `list_tables() -> Dict[str, str]`: 列出所有表及其类型
- `drop_table(table_name: str) -> None`: 删除表
## 错误处理
所有存储操作都可能引发 `StoreError` 异常:
```python
from funtable.kv.interface import StoreError
try:
table.set("key", {"data": "value"})
except StoreError as e:
print(f"存储错误: {e}")
if e.cause:
print(f"原因: {e.cause}")
```
## 贡献
1. Fork 仓库
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 打开 Pull Request
## 许可证
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
## 链接
- **仓库**: https://github.com/farfarfun/funtable
- **PyPI**: https://pypi.org/project/funtable/
- **问题**: https://github.com/farfarfun/funtable/issues
- **发布**: https://github.com/farfarfun/funtable/releases
Raw data
{
"_id": null,
"home_page": null,
"name": "funtable",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "\u725b\u54e5 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>",
"keywords": "table, storage, database, kv, key-value, sqlite, tinydb, sqlmodel, orm, abstraction",
"author": null,
"author_email": "\u725b\u54e5 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>",
"download_url": null,
"platform": null,
"description": "# FunTable\n\n[](https://badge.fury.io/py/funtable)\n[](https://pypi.org/project/funtable/)\n[](https://github.com/farfarfun/funtable/blob/main/LICENSE)\n\n\u4e00\u4e2a\u7edf\u4e00\u7684\u8868\u5b58\u50a8\u62bd\u8c61\u5e93\uff0c\u4e3a\u5404\u79cd\u5b58\u50a8\u540e\u7aef\uff08\u5305\u62ec SQLite\u3001TinyDB\u3001SQLModel \u548c\u4e91\u5b58\u50a8\u5feb\u7167\uff09\u63d0\u4f9b\u4e00\u81f4\u7684\u63a5\u53e3\u3002\n\n## \u7279\u6027\n\n- **\u7edf\u4e00\u63a5\u53e3**: \u8de8\u4e0d\u540c\u5b58\u50a8\u540e\u7aef\u7684\u4e00\u81f4 API\n- **\u591a\u79cd\u5b58\u50a8\u7c7b\u578b**: \u652f\u6301 KV\uff08\u952e\u503c\uff09\u548c KKV\uff08\u952e\u952e\u503c\uff09\u5b58\u50a8\u6a21\u5f0f\n- **\u4e8b\u52a1\u652f\u6301**: \u5185\u7f6e\u4e8b\u52a1\u7ba1\u7406\uff0c\u786e\u4fdd\u6570\u636e\u4e00\u81f4\u6027\n- **\u591a\u79cd\u540e\u7aef**: \u652f\u6301 SQLite\u3001TinyDB\u3001SQLModel \u548c\u4e91\u5b58\u50a8\n- **\u5feb\u7167\u7ba1\u7406**: \u81ea\u52a8\u5907\u4efd\u548c\u7248\u672c\u7ba1\u7406\uff0c\u652f\u6301\u4e91\u5b58\u50a8\n- **\u7ebf\u7a0b\u5b89\u5168**: \u4e3a\u5e76\u53d1\u8bbf\u95ee\u573a\u666f\u800c\u8bbe\u8ba1\n\n## \u5b89\u88c5\n\n### \u57fa\u7840\u5b89\u88c5\n\n```bash\npip install funtable\n```\n\n### \u5b89\u88c5\u53ef\u9009\u4f9d\u8d56\n\n```bash\n# \u652f\u6301 SQLModel\npip install funtable[sqlmodel]\n\n# \u652f\u6301\u5feb\u7167/\u4e91\u5b58\u50a8\npip install funtable[snapshot]\n\n# \u652f\u6301 TinyDB\npip install funtable[kv]\n\n# \u5b89\u88c5\u6240\u6709\u53ef\u9009\u4f9d\u8d56\npip install funtable[sqlmodel,snapshot,kv]\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n### KV\uff08\u952e\u503c\uff09\u5b58\u50a8\n\n```python\nfrom funtable.kv import SQLiteDB\n\n# \u521d\u59cb\u5316\u6570\u636e\u5e93\ndb = SQLiteDB(\"my_database.db\")\n\n# \u521b\u5efa KV \u8868\ndb.create_kv_table(\"users\")\ntable = db.get_table(\"users\")\n\n# \u5b58\u50a8\u6570\u636e\ntable.set(\"user1\", {\"name\": \"Alice\", \"age\": 30})\ntable.set(\"user2\", {\"name\": \"Bob\", \"age\": 25})\n\n# \u68c0\u7d22\u6570\u636e\nuser = table.get(\"user1\")\nprint(user) # {\"name\": \"Alice\", \"age\": 30}\n\n# \u5217\u51fa\u6240\u6709\u952e\nkeys = table.list_keys()\nprint(keys) # [\"user1\", \"user2\"]\n\n# \u83b7\u53d6\u6240\u6709\u6570\u636e\nall_data = table.list_all()\nprint(all_data)\n```\n\n### KKV\uff08\u952e\u952e\u503c\uff09\u5b58\u50a8\n\n```python\nfrom funtable.kv import SQLiteDB\n\n# \u521d\u59cb\u5316\u6570\u636e\u5e93\ndb = SQLiteDB(\"my_database.db\")\n\n# \u521b\u5efa KKV \u8868\ndb.create_kkv_table(\"user_profiles\")\ntable = db.get_table(\"user_profiles\")\n\n# \u4f7f\u7528\u4e24\u7ea7\u952e\u5b58\u50a8\u6570\u636e\ntable.set(\"user1\", \"profile\", {\"bio\": \"Software Engineer\"})\ntable.set(\"user1\", \"settings\", {\"theme\": \"dark\", \"notifications\": True})\ntable.set(\"user2\", \"profile\", {\"bio\": \"Data Scientist\"})\n\n# \u68c0\u7d22\u6570\u636e\nprofile = table.get(\"user1\", \"profile\")\nprint(profile) # {\"bio\": \"Software Engineer\"}\n\n# \u5217\u51fa\u4e3b\u952e\npkeys = table.list_pkeys()\nprint(pkeys) # [\"user1\", \"user2\"]\n\n# \u5217\u51fa\u67d0\u4e2a\u4e3b\u952e\u7684\u6b21\u952e\nskeys = table.list_skeys(\"user1\")\nprint(skeys) # [\"profile\", \"settings\"]\n```\n\n### \u4e8b\u52a1\u652f\u6301\n\n```python\n# \u4f7f\u7528\u4e8b\u52a1\u786e\u4fdd\u6570\u636e\u4e00\u81f4\u6027\ntable.begin_transaction()\ntry:\n table.set(\"user3\", {\"name\": \"Charlie\", \"age\": 35})\n table.set(\"user4\", {\"name\": \"David\", \"age\": 40})\n table.commit()\nexcept Exception as e:\n table.rollback()\n print(f\"\u4e8b\u52a1\u5931\u8d25: {e}\")\n```\n\n### SQLModel \u96c6\u6210\n\n```python\nfrom funtable.sqlmodel import BaseModel\nfrom sqlmodel import Field\nfrom datetime import datetime\n\nclass User(BaseModel, table=True):\n name: str = Field(description=\"\u7528\u6237\u540d\")\n email: str = Field(description=\"\u7528\u6237\u90ae\u7bb1\")\n age: int = Field(description=\"\u7528\u6237\u5e74\u9f84\")\n\n# BaseModel \u63d0\u4f9b\u5185\u7f6e\u5b57\u6bb5:\n# - id: \u81ea\u589e\u4e3b\u952e\n# - uid: \u552f\u4e00\u6807\u8bc6\u7b26\n# - gmt_create: \u521b\u5efa\u65f6\u95f4\u6233\n# - gmt_modified: \u4fee\u6539\u65f6\u95f4\u6233\n\n# \u4e0e SQLModel session \u4e00\u8d77\u4f7f\u7528\nfrom sqlmodel import create_engine, Session\n\nengine = create_engine(\"sqlite:///users.db\")\nwith Session(engine) as session:\n user = User(name=\"Alice\", email=\"alice@example.com\", age=30)\n session.add(user)\n session.commit()\n \n # \u67e5\u8be2\u65b9\u6cd5\n all_users = User.all(session)\n user_by_id = User.by_id(1, session)\n user_by_uid = User.by_uid(\"some-uid\", session)\n```\n\n### \u5feb\u7167\u7ba1\u7406\n\n```python\nfrom funtable.snapshot import DriveSnapshot\nfrom fundrive import SomeDriveImplementation\n\n# \u521d\u59cb\u5316\u9a71\u52a8\u5668\u548c\u5feb\u7167\u7ba1\u7406\u5668\ndrive = SomeDriveImplementation()\nsnapshot = DriveSnapshot(\n table_fid=\"my-table-id\",\n drive=drive,\n num=7 # \u4fdd\u7559 7 \u4e2a\u7248\u672c\n)\n\n# \u521b\u5efa\u5feb\u7167\nsnapshot.update(\"/path/to/data\", partition=\"20231201\")\n\n# \u4e0b\u8f7d\u6700\u65b0\u5feb\u7167\nsnapshot.download(\"/path/to/restore\")\n```\n\n## \u5b58\u50a8\u540e\u7aef\n\n### SQLite \u540e\u7aef\n- **\u6587\u4ef6**: `funtable.kv.sqlite_table`\n- **\u7279\u6027**: ACID \u4e8b\u52a1\u3001SQL \u67e5\u8be2\u3001\u57fa\u4e8e\u6587\u4ef6\u7684\u5b58\u50a8\n- **\u9002\u7528\u4e8e**: \u751f\u4ea7\u5e94\u7528\u3001\u590d\u6742\u67e5\u8be2\u3001\u6570\u636e\u5b8c\u6574\u6027\n\n### TinyDB \u540e\u7aef\n- **\u6587\u4ef6**: `funtable.kv.tinydb_table`\n- **\u7279\u6027**: \u57fa\u4e8e JSON\u3001\u8f7b\u91cf\u7ea7\u3001\u65e0\u5916\u90e8\u4f9d\u8d56\n- **\u9002\u7528\u4e8e**: \u5c0f\u578b\u5e94\u7528\u3001\u539f\u578b\u5f00\u53d1\u3001\u7b80\u5355\u6570\u636e\u7ed3\u6784\n\n### SQLModel \u96c6\u6210\n- **\u6587\u4ef6**: `funtable.sqlmodel.base`\n- **\u7279\u6027**: ORM \u529f\u80fd\u3001\u7c7b\u578b\u5b89\u5168\u3001Pydantic \u96c6\u6210\n- **\u9002\u7528\u4e8e**: \u73b0\u4ee3 Python \u5e94\u7528\u3001API \u5f00\u53d1\u3001\u6570\u636e\u9a8c\u8bc1\n\n### \u4e91\u5b58\u50a8\u5feb\u7167\n- **\u6587\u4ef6**: `funtable.snapshot.core`\n- **\u7279\u6027**: \u81ea\u52a8\u5907\u4efd\u3001\u7248\u672c\u7ba1\u7406\u3001\u4e91\u96c6\u6210\n- **\u9002\u7528\u4e8e**: \u6570\u636e\u5907\u4efd\u3001\u707e\u96be\u6062\u590d\u3001\u5206\u5e03\u5f0f\u7cfb\u7edf\n\n## API \u53c2\u8003\n\n### \u6838\u5fc3\u63a5\u53e3\n\n#### BaseKVTable\n- `set(key: str, value: Dict) -> None`: \u5b58\u50a8\u952e\u503c\u5bf9\n- `get(key: str) -> Optional[Dict]`: \u901a\u8fc7\u952e\u68c0\u7d22\u503c\n- `delete(key: str) -> bool`: \u5220\u9664\u952e\u503c\u5bf9\n- `list_keys() -> List[str]`: \u83b7\u53d6\u6240\u6709\u952e\n- `list_all() -> Dict[str, Dict]`: \u83b7\u53d6\u6240\u6709\u952e\u503c\u5bf9\n- `begin_transaction()`, `commit()`, `rollback()`: \u4e8b\u52a1\u7ba1\u7406\n\n#### BaseKKVTable\n- `set(pkey: str, skey: str, value: Dict) -> None`: \u4f7f\u7528\u4e24\u7ea7\u952e\u5b58\u50a8\n- `get(pkey: str, skey: str) -> Optional[Dict]`: \u901a\u8fc7\u4e24\u7ea7\u952e\u68c0\u7d22\n- `delete(pkey: str, skey: str) -> bool`: \u901a\u8fc7\u4e24\u7ea7\u952e\u5220\u9664\n- `list_pkeys() -> List[str]`: \u83b7\u53d6\u6240\u6709\u4e3b\u952e\n- `list_skeys(pkey: str) -> List[str]`: \u83b7\u53d6\u4e3b\u952e\u7684\u6b21\u952e\n- `list_all() -> Dict[str, Dict[str, Dict]]`: \u83b7\u53d6\u6240\u6709\u6570\u636e\n- `batch_set(items: Dict) -> None`: \u6279\u91cf\u63d2\u5165\u64cd\u4f5c\n- `batch_delete(items: List[tuple]) -> None`: \u6279\u91cf\u5220\u9664\u64cd\u4f5c\n\n#### BaseDB\n- `create_kv_table(table_name: str) -> None`: \u521b\u5efa KV \u8868\n- `create_kkv_table(table_name: str) -> None`: \u521b\u5efa KKV \u8868\n- `get_table(table_name: str) -> Union[BaseKVTable, BaseKKVTable]`: \u83b7\u53d6\u8868\u5b9e\u4f8b\n- `list_tables() -> Dict[str, str]`: \u5217\u51fa\u6240\u6709\u8868\u53ca\u5176\u7c7b\u578b\n- `drop_table(table_name: str) -> None`: \u5220\u9664\u8868\n\n## \u9519\u8bef\u5904\u7406\n\n\u6240\u6709\u5b58\u50a8\u64cd\u4f5c\u90fd\u53ef\u80fd\u5f15\u53d1 `StoreError` \u5f02\u5e38\uff1a\n\n```python\nfrom funtable.kv.interface import StoreError\n\ntry:\n table.set(\"key\", {\"data\": \"value\"})\nexcept StoreError as e:\n print(f\"\u5b58\u50a8\u9519\u8bef: {e}\")\n if e.cause:\n print(f\"\u539f\u56e0: {e.cause}\")\n```\n\n## \u8d21\u732e\n\n1. Fork \u4ed3\u5e93\n2. \u521b\u5efa\u529f\u80fd\u5206\u652f (`git checkout -b feature/amazing-feature`)\n3. \u63d0\u4ea4\u66f4\u6539 (`git commit -m 'Add amazing feature'`)\n4. \u63a8\u9001\u5230\u5206\u652f (`git push origin feature/amazing-feature`)\n5. \u6253\u5f00 Pull Request\n\n## \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 MIT \u8bb8\u53ef\u8bc1 - \u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6\u3002\n\n## \u94fe\u63a5\n\n- **\u4ed3\u5e93**: https://github.com/farfarfun/funtable\n- **PyPI**: https://pypi.org/project/funtable/\n- **\u95ee\u9898**: https://github.com/farfarfun/funtable/issues\n- **\u53d1\u5e03**: https://github.com/farfarfun/funtable/releases\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A unified table storage abstraction library with support for multiple backends",
"version": "1.0.41",
"project_urls": {
"Organization": "https://github.com/farfarfun",
"Releases": "https://github.com/farfarfun/funtable/releases",
"Repository": "https://github.com/farfarfun/funtable"
},
"split_keywords": [
"table",
" storage",
" database",
" kv",
" key-value",
" sqlite",
" tinydb",
" sqlmodel",
" orm",
" abstraction"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "45a66760c69ec39147e7299a236bccf9205ced845164a9f76205ae45a778d118",
"md5": "67e6902c537e0da81d445347669f4e3e",
"sha256": "e4f40cae517a746d9f2b68359e7cd032dc4ccf3b2b7832724f3aa7d3ef812be2"
},
"downloads": -1,
"filename": "funtable-1.0.41-py3-none-any.whl",
"has_sig": false,
"md5_digest": "67e6902c537e0da81d445347669f4e3e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 18537,
"upload_time": "2025-08-23T06:28:23",
"upload_time_iso_8601": "2025-08-23T06:28:23.612478Z",
"url": "https://files.pythonhosted.org/packages/45/a6/6760c69ec39147e7299a236bccf9205ced845164a9f76205ae45a778d118/funtable-1.0.41-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-23 06:28:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "farfarfun",
"github_project": "funtable",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "funtable"
}