Add MySQL support to backend: update dependencies, enhance README with database initialization instructions, and include .env configuration examples.
This commit is contained in:
1 parent
f1b4ee5268
commit
e9edba092b
9 files changed
+86
-13
No files matched your search
@@ -21,3 +21,4 @@ venv/
|
||||
htmlcov/
|
||||
.jellyfish.db
|
||||
jellyfish.db
|
||||
.env
|
||||
@@ -12,6 +12,8 @@ API_V1_PREFIX=/api/v1
|
||||
# DATABASE_URL=sqlite+aiosqlite:///./jellyfish.db
|
||||
# PostgreSQL 示例:
|
||||
# DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/jellyfish
|
||||
# MySQL 示例(使用 aiomysql 驱动):
|
||||
# DATABASE_URL=mysql+aiomysql://user:pass@localhost:3306/jellyfish
|
||||
|
||||
# CORS 前端地址(逗号分隔)
|
||||
# CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
||||
|
||||
+30
-1
@@ -58,7 +58,36 @@ cp .env.example .env
|
||||
# 按需编辑 .env
|
||||
```
|
||||
|
||||
### 4. 启动服务
|
||||
### 4. 初始化数据库(可选,MySQL/PostgreSQL 等)
|
||||
|
||||
如果你使用的是 **SQLite 默认配置**(`DATABASE_URL=sqlite+aiosqlite:///./jellyfish.db`),可以跳过本节,首次访问时会自动创建文件。
|
||||
|
||||
若切换到 **MySQL / PostgreSQL 等外部数据库**,建议先手动初始化表结构:
|
||||
|
||||
1. 在 `.env` 中配置数据库连接(示例):
|
||||
|
||||
```env
|
||||
# SQLite(默认)
|
||||
# DATABASE_URL=sqlite+aiosqlite:///./jellyfish.db
|
||||
|
||||
# MySQL(使用 aiomysql 驱动)
|
||||
# DATABASE_URL=mysql+aiomysql://user:pass@localhost:3306/jellyfish
|
||||
|
||||
# PostgreSQL
|
||||
# DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/jellyfish
|
||||
```
|
||||
|
||||
2. 运行初始化脚本(使用 uv):
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
uv sync # 确保依赖已安装
|
||||
uv run python init_db.py
|
||||
```
|
||||
|
||||
该脚本会导入所有 ORM 模型并调用 `Base.metadata.create_all()`,在目标数据库中创建所需的 27 张业务表。
|
||||
|
||||
### 5. 启动服务
|
||||
|
||||
```bash
|
||||
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
@@ -33,6 +33,10 @@ class Base(DeclarativeBase):
|
||||
|
||||
async def init_db() -> None:
|
||||
"""创建所有表(开发/迁移用)。"""
|
||||
# 确保 ORM 模型已导入,从而注册到 Base.metadata
|
||||
import app.models.llm # noqa: F401
|
||||
import app.models.studio # noqa: F401
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from app.core.db import Base
|
||||
from app.models.base import TimestampMixin
|
||||
|
||||
from app.models.llm import Agent, Model, ModelSettings, Provider
|
||||
from app.models.llm import Model, ModelSettings, Provider
|
||||
from app.models.studio import (
|
||||
Actor,
|
||||
ActorImage,
|
||||
@@ -58,7 +58,6 @@ __all__ = [
|
||||
"PromptTemplate",
|
||||
"FileItem",
|
||||
"TimelineClip",
|
||||
"Agent",
|
||||
"Provider",
|
||||
"Model",
|
||||
"ModelSettings",
|
||||
|
||||
@@ -499,7 +499,7 @@ class Costume(Base, TimestampMixin):
|
||||
)
|
||||
|
||||
|
||||
class Shot(Base):
|
||||
class Shot(Base,TimestampMixin):
|
||||
"""镜头表(基础信息)。
|
||||
|
||||
说明:
|
||||
@@ -572,7 +572,7 @@ class Shot(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotDetail(Base):
|
||||
class ShotDetail(Base,TimestampMixin):
|
||||
"""镜头细节(1:1)。
|
||||
|
||||
设计点:
|
||||
@@ -642,7 +642,7 @@ class ShotDetail(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotDialogLine(Base):
|
||||
class ShotDialogLine(Base,TimestampMixin):
|
||||
"""镜头对话行。
|
||||
|
||||
设计点:
|
||||
@@ -789,7 +789,7 @@ class Character(Base, TimestampMixin):
|
||||
)
|
||||
|
||||
|
||||
class CharacterPropLink(Base):
|
||||
class CharacterPropLink(Base,TimestampMixin):
|
||||
"""角色与道具绑定(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -825,7 +825,7 @@ class CharacterPropLink(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotCharacterLink(Base):
|
||||
class ShotCharacterLink(Base,TimestampMixin):
|
||||
"""镜头引用角色(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -1021,7 +1021,7 @@ class CostumeImage(Base, TimestampMixin):
|
||||
)
|
||||
|
||||
|
||||
class ShotActorImageLink(Base):
|
||||
class ShotActorImageLink(Base,TimestampMixin):
|
||||
"""镜头引用演员形象/立绘(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -1045,7 +1045,7 @@ class ShotActorImageLink(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotSceneLink(Base):
|
||||
class ShotSceneLink(Base,TimestampMixin):
|
||||
"""镜头引用场景(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -1069,7 +1069,7 @@ class ShotSceneLink(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotPropLink(Base):
|
||||
class ShotPropLink(Base,TimestampMixin):
|
||||
"""镜头引用道具(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -1093,7 +1093,7 @@ class ShotPropLink(Base):
|
||||
)
|
||||
|
||||
|
||||
class ShotCostumeLink(Base):
|
||||
class ShotCostumeLink(Base,TimestampMixin):
|
||||
"""镜头引用服装(多对多)。
|
||||
|
||||
应用层保证:
|
||||
@@ -1131,7 +1131,7 @@ class PromptCategory(str, Enum):
|
||||
combined = "combined"
|
||||
|
||||
|
||||
class PromptTemplate(Base):
|
||||
class PromptTemplate(Base,TimestampMixin):
|
||||
"""提示词模板表。"""
|
||||
|
||||
__tablename__ = "prompt_templates"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import asyncio
|
||||
|
||||
from app.core.db import init_db, close_db
|
||||
|
||||
|
||||
async def _main() -> None:
|
||||
"""初始化数据库,创建所有表。"""
|
||||
await init_db()
|
||||
await close_db()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(_main())
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies = [
|
||||
"langgraph>=0.2.0",
|
||||
"sqlalchemy[asyncio]>=2.0.0",
|
||||
"aiosqlite>=0.20.0",
|
||||
"aiomysql>=0.3.0",
|
||||
"pydantic>=2.0.0",
|
||||
"pydantic-settings>=2.0.0",
|
||||
"python-dotenv>=1.0.0",
|
||||
|
||||
Generated
+23
@@ -2,6 +2,18 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "aiomysql"
|
||||
version = "0.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pymysql" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/29/e0/302aeffe8d90853556f47f3106b89c16cc2ec2a4d269bdfd82e3f4ae12cc/aiomysql-0.3.2.tar.gz", hash = "sha256:72d15ef5cfc34c03468eb41e1b90adb9fd9347b0b589114bd23ead569a02ac1a", size = 108311, upload-time = "2025-10-22T00:15:21.278Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/af/aae0153c3e28712adaf462328f6c7a3c196a1c1c27b491de4377dd3e6b52/aiomysql-0.3.2-py3-none-any.whl", hash = "sha256:c82c5ba04137d7afd5c693a258bea8ead2aad77101668044143a991e04632eb2", size = 71834, upload-time = "2025-10-22T00:15:15.905Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiosqlite"
|
||||
version = "0.22.1"
|
||||
@@ -318,6 +330,7 @@ name = "jellyfish-backend"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiomysql" },
|
||||
{ name = "aiosqlite" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "langchain" },
|
||||
@@ -348,6 +361,7 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiomysql", specifier = ">=0.3.0" },
|
||||
{ name = "aiosqlite", specifier = ">=0.20.0" },
|
||||
{ name = "fastapi", specifier = ">=0.115.0" },
|
||||
{ name = "httpx", marker = "extra == 'dev'", specifier = ">=0.27.0" },
|
||||
@@ -890,6 +904,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pymysql"
|
||||
version = "1.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
|
||||
Reference in new issue
Block a user