first commit

This commit is contained in:
2026-06-25 17:41:06 +08:00
commit b2f23a933b
370 changed files with 30526 additions and 0 deletions
@@ -0,0 +1,86 @@
<%
# 对 entity 中的 name 进行大小写处理,生成模型名、路由名和模式名
model_name = entity.name.capitalize()
endpoint = entity.name.lower()
router_name = endpoint + '_router'
schema_name = model_name + 'Schema'
# 处理实体关系
relations = generator._process_relations(entity) if hasattr(generator, '_process_relations') else []
%>
# 导入必要的模块和类
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
# 导入数据库会话依赖和相关模型、模式
from ..database import get_db
from ..models.${endpoint} import ${model_name}
from ..schemas.${endpoint} import ${schema_name}, ${model_name}Create, ${model_name}Update
# 创建 APIRouter 实例
${router_name} = APIRouter(
prefix="/api/${endpoint}s",
tags=["${model_name}管理"],
# 根据配置添加依赖项
% if hasattr(entity, 'api') and hasattr(entity.api, 'auth_required') and entity.api.auth_required:
dependencies=[Depends(get_current_active_user)],
% endif
)
# 定义获取实体列表的路由
@${router_name}.get("/", response_model=List[${schema_name}])
def list_${endpoint}s(
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db)
):
"""
获取${model_name}列表
- **skip**: 跳过多少条记录
- **limit**: 返回最多多少条记录
"""
return db.query(${model_name}).offset(skip).limit(limit).all()
# 定义创建新实体的路由
@${router_name}.post("/", response_model=${schema_name})
def create_${endpoint}(
${endpoint}: ${model_name}Create,
db: Session = Depends(get_db)
% if hasattr(entity, 'api') and hasattr(entity.api, 'auth_required') and entity.api.auth_required:
, current_user: User = Depends(get_current_active_user)
% endif
):
"""
创建新的${model_name}
"""
db_${endpoint} = ${model_name}(**${endpoint}.dict())
db.add(db_${endpoint})
db.commit()
db.refresh(db_${endpoint})
return db_${endpoint}
# 其他 CRUD 操作...
% for rel in relations:
# 定义获取实体关联关系的路由
@${router_name}.get(
"/{${endpoint}_id}/${rel['name']}",
response_model=
% if rel['type'] in ('one-to-many', 'many-to-many'):
List[${rel['target'].capitalize()}Schema]
% else:
${rel['target'].capitalize()}Schema
% endif
)
def get_${endpoint}_${rel['name']}(
${endpoint}_id: int,
db: Session = Depends(get_db)
):
"""
获取${model_name}的${rel['name']}
"""
entity_obj = db.query(${model_name}).get(${endpoint}_id)
if not entity_obj:
raise HTTPException(status_code=404, detail="${model_name} not found")
return entity_obj.${rel['name']}
% endfor
@@ -0,0 +1,71 @@
<%
model_name = entity.name.capitalize()
relations = generator._process_relations(entity)
%>
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime, Boolean
from sqlalchemy.orm import relationship
from ..database import Base
class ${model_name}(Base):
"""
${entity.description or model_name + ' 模型'}
对应数据库表: ${entity.name.lower()}s
"""
__tablename__ = "${entity.name.lower()}s"
# 主键字段
% if not any(field['primary_key'] for field in entity.fields):
id = Column(Integer, primary_key=True, index=True, doc="主键ID")
% endif
% for field in entity.fields:
## 字段定义: ${field['name']}
${field['name']} = Column(
${'String' if field['type'] == 'String' else 'Integer' if field['type'] in ('Long', 'Integer') else 'Boolean' if field['type'] == 'Boolean' else 'DateTime' if field['type'] == 'DateTime' else 'String'},
% if field['type'] == 'String' and 'length' in field:
length=${field['length']},
% endif
% if 'default' in field:
default=${field['default']},
% endif
% if 'primary_key' in field:
primary_key=True,
% endif
% if 'unique' in field and field['unique']:
unique=True,
% endif
% if 'nullable' in field and not field['nullable']:
nullable=False,
% endif
% if 'index' in field and field['index']:
index=True,
% endif
doc="${field['description'] or field['name'] + '字段'}"
)
% endfor
# 关系定义
% for rel in relations:
## ${rel['type']} 关系: ${rel['name']}
% if rel['type'] == 'one-to-many':
${rel['name']} = relationship(
"${rel['target'].capitalize()}",
back_populates="${rel['reverse_name']}",
doc="${rel['description'] or model_name + '的' + rel['name']}"
)
% elif rel['type'] == 'many-to-one':
${rel['name']}_id = Column(Integer, ForeignKey("${rel['target']}s.id"))
${rel['name']} = relationship(
"${rel['target'].capitalize()}",
back_populates="${rel['reverse_name']}",
doc="${rel['description'] or '关联的' + rel['target']}"
)
% elif rel['type'] == 'many-to-many':
${rel['name']} = relationship(
"${rel['target'].capitalize()}",
secondary="${rel['through']}",
back_populates="${rel['reverse_name']}",
doc="${rel['description'] or model_name + '的多对多关联'}"
)
% endif
% endfor
@@ -0,0 +1,61 @@
<%
schema_name = entity.name.capitalize() + 'Schema'
create_schema_name = entity.name.capitalize() + 'Create'
update_schema_name = entity.name.capitalize() + 'Update'
relations = generator._process_relations(entity)
%>
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
class ${schema_name}(BaseModel):
"""
${entity.description or schema_name + ' 返回模型'}
包含所有字段和关联字段
"""
id: int
% for field in entity.fields:
${field['name']}: ${'str' if field['type'] == 'String' else 'int' if field['type'] in ('Long', 'Integer') else 'bool' if field['type'] == 'Boolean' else 'datetime' if field['type'] == 'DateTime' else 'str'}
% endfor
# 关联字段
% for rel in relations:
% if rel['type'] in ('one-to-many', 'many-to-many'):
${rel['name']}: List['${rel['target'].capitalize()}Schema'] = []
% elif rel['type'] == 'many-to-one':
${rel['name']}: Optional['${rel['target'].capitalize()}Schema'] = None
% endif
% endfor
class Config:
orm_mode = True
schema_extra = {
"example": {
"id": 1,
% for field in entity.fields:
"${field['name']}": ${'"sample"' if field['type'] == 'String' else 1 if field['type'] in ('Long', 'Integer') else True if field['type'] == 'Boolean' else '"2023-01-01T00:00:00"' if field['type'] == 'DateTime' else '"sample"'},
% endfor
}
}
class ${create_schema_name}(BaseModel):
"""
${entity.description or create_schema_name + ' 创建模型'}
不包含只读字段
"""
% for field in entity.fields:
% if not hasattr(field, 'primary_key') or not field['primary_key']:
${field['name']}: ${'str' if field['type'] == 'String' else 'int' if field['type'] in ('Long', 'Integer') else 'bool' if field['type'] == 'Boolean' else 'datetime' if field['type'] == 'DateTime' else 'str'}
% endif
% endfor
class ${update_schema_name}(BaseModel):
"""
${entity.description or update_schema_name + ' 更新模型'}
所有字段都是可选的
"""
% for field in entity.fields:
% if not hasattr(field, 'primary_key') or not field['primary_key']:
${field['name']}: Optional[${'str' if field['type'] == 'String' else 'int' if field['type'] in ('Long', 'Integer') else 'bool' if field['type'] == 'Boolean' else 'datetime' if field['type'] == 'DateTime' else 'str'}]
% endif
% endfor
@@ -0,0 +1,46 @@
<%
model_name = entity.name.capitalize()
endpoint = '/' + entity.name.lower() + 's'
%>
import pytest
from fastapi.testclient import TestClient
def test_create_${entity.name.lower()}(client: TestClient):
response = client.post(
"${endpoint}/",
json={
% for field in entity.fields:
% if field['name'] != 'id':
"${field['name']}": ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'},
% endif
% endfor
},
)
assert response.status_code == 200
data = response.json()
assert "id" in data
% for field in entity.fields:
% if field['name'] != 'id':
assert data["${field['name']}"] == ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'}
% endif
% endfor
def test_get_${entity.name.lower()}(client: TestClient):
# 先创建一个测试数据
create_response = client.post(
"${endpoint}/",
json={
% for field in entity.fields:
% if field['name'] != 'id':
"${field['name']}": ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'},
% endif
% endfor
},
)
${entity.name.lower()}_id = create_response.json()["id"]
# 测试获取
response = client.get(f"${endpoint}/{${entity.name.lower()}_id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == ${entity.name.lower()}_id
@@ -0,0 +1,51 @@
<%
component_name = entity.name.capitalize() + 'List'
api_path = '/' + entity.name.lower() + 's'
%>
<template>
<div class="${entity.name.lower()}-container">
<h2>${component_name}</h2>
<el-table :data="list" border style="width: 100%">
% for field in entity.fields:
<el-table-column prop="${field['name']}" label="${field['name'].capitalize()}" width="180" />
% endfor
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { get${entity.name.capitalize()}List, delete${entity.name.capitalize()} } from '@/api/${entity.name.lower()}'
const list = ref([])
onMounted(() => {
fetchData()
})
const fetchData = async () => {
const res = await get${entity.name.capitalize()}List()
list.value = res.data
}
const handleEdit = (row) => {
// 编辑逻辑
}
const handleDelete = async (row) => {
await delete${entity.name.capitalize()}(row.id)
fetchData()
}
</script>
<style scoped>
.${entity.name.lower()}-container {
padding: 20px;
}
</style>
@@ -0,0 +1,73 @@
<%
model_name = entity.name.capitalize()
router_name = entity.name.lower() + '_router'
schema_name = model_name + 'Schema'
table_name = entity.name.lower() + 's'
%>
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from ..database import get_db
from ..models.${entity.name.lower()} import ${model_name}
from ..schemas.${entity.name.lower()} import ${schema_name}
${router_name} = APIRouter(
prefix="/api/${entity.name.lower()}s",
tags=["${entity.name.capitalize()}管理"]
)
@${router_name}.get("/", response_model=List[${schema_name}])
def get_${entity.name.lower()}_list(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
"""
获取${entity.name.capitalize()}列表
"""
return db.query(${model_name}).offset(skip).limit(limit).all()
@${router_name}.post("/", response_model=${schema_name})
def create_${entity.name.lower()}(${entity.name.lower()}: ${schema_name}, db: Session = Depends(get_db)):
"""
创建${entity.name.capitalize()}
"""
db_${entity.name.lower()} = ${model_name}(**${entity.name.lower()}.dict())
db.add(db_${entity.name.lower()})
db.commit()
db.refresh(db_${entity.name.lower()})
return db_${entity.name.lower()}
@${router_name}.get("/{${entity.name.lower()}_id}", response_model=${schema_name})
def get_${entity.name.lower()}(${entity.name.lower()}_id: int, db: Session = Depends(get_db)):
"""
获取单个${entity.name.capitalize()}
"""
${entity.name.lower()} = db.query(${model_name}).filter(${model_name}.id == ${entity.name.lower()}_id).first()
if not ${entity.name.lower()}:
raise HTTPException(status_code=404, detail="${entity.name.capitalize()} not found")
return ${entity.name.lower()}
@${router_name}.put("/{${entity.name.lower()}_id}", response_model=${schema_name})
def update_${entity.name.lower()}(${entity.name.lower()}_id: int, ${entity.name.lower()}: ${schema_name}, db: Session = Depends(get_db)):
"""
更新${entity.name.capitalize()}
"""
db_${entity.name.lower()} = db.query(${model_name}).filter(${model_name}.id == ${entity.name.lower()}_id).first()
if not db_${entity.name.lower()}:
raise HTTPException(status_code=404, detail="${entity.name.capitalize()} not found")
% for field in entity.fields:
setattr(db_${entity.name.lower()}, "${field['name']}", ${entity.name.lower()}.${field['name']})
% endfor
db.commit()
db.refresh(db_${entity.name.lower()})
return db_${entity.name.lower()}
@${router_name}.delete("/{${entity.name.lower()}_id}")
def delete_${entity.name.lower()}(${entity.name.lower()}_id: int, db: Session = Depends(get_db)):
"""
删除${entity.name.capitalize()}
"""
${entity.name.lower()} = db.query(${model_name}).filter(${model_name}.id == ${entity.name.lower()}_id).first()
if not ${entity.name.lower()}:
raise HTTPException(status_code=404, detail="${entity.name.capitalize()} not found")
db.delete(${entity.name.lower()})
db.commit()
return {"message": "${entity.name.capitalize()} deleted successfully"}
@@ -0,0 +1,21 @@
<%
model_name = entity.name.capitalize()
%>
from sqlalchemy import Column, Integer, String
from ..database import Base
class ${model_name}(Base):
__tablename__ = "${entity.name.lower()}s"
id = Column(Integer, primary_key=True, index=True)
% for field in entity.fields:
% if field['type'] == 'String':
${field['name']} = Column(String)
% elif field['type'] == 'Long':
${field['name']} = Column(Integer)
% elif field['type'] == 'Boolean':
${field['name']} = Column(Boolean)
% else:
${field['name']} = Column(String) # 默认使用String类型
% endif
% endfor
@@ -0,0 +1,30 @@
<%
schema_name = entity.name.capitalize() + 'Schema'
create_schema_name = entity.name.capitalize() + 'Create'
update_schema_name = entity.name.capitalize() + 'Update'
%>
from pydantic import BaseModel
from typing import Optional
class ${schema_name}(BaseModel):
id: int
% for field in entity.fields:
${field['name']}: ${'str' if field['type'] == 'String' else 'int' if field['type'] == 'Long' else 'bool' if field['type'] == 'Boolean' else 'str'}
% endfor
class Config:
orm_mode = True
class ${create_schema_name}(BaseModel):
% for field in entity.fields:
% if field['name'] != 'id':
${field['name']}: ${'str' if field['type'] == 'String' else 'int' if field['type'] == 'Long' else 'bool' if field['type'] == 'Boolean' else 'str'}
% endif
% endfor
class ${update_schema_name}(BaseModel):
% for field in entity.fields:
% if field['name'] != 'id':
${field['name']}: Optional[${'str' if field['type'] == 'String' else 'int' if field['type'] == 'Long' else 'bool' if field['type'] == 'Boolean' else 'str'}]
% endif
% endfor
@@ -0,0 +1,46 @@
<%
model_name = entity.name.capitalize()
endpoint = '/' + entity.name.lower() + 's'
%>
import pytest
from fastapi.testclient import TestClient
def test_create_${entity.name.lower()}(client: TestClient):
response = client.post(
"${endpoint}/",
json={
% for field in entity.fields:
% if field['name'] != 'id':
"${field['name']}": ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'},
% endif
% endfor
},
)
assert response.status_code == 200
data = response.json()
assert "id" in data
% for field in entity.fields:
% if field['name'] != 'id':
assert data["${field['name']}"] == ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'}
% endif
% endfor
def test_get_${entity.name.lower()}(client: TestClient):
# 先创建一个测试数据
create_response = client.post(
"${endpoint}/",
json={
% for field in entity.fields:
% if field['name'] != 'id':
"${field['name']}": ${'"test"' if field['type'] == 'String' else '1' if field['type'] == 'Long' else 'True' if field['type'] == 'Boolean' else '"test"'},
% endif
% endfor
},
)
${entity.name.lower()}_id = create_response.json()["id"]
# 测试获取
response = client.get(f"${endpoint}/{${entity.name.lower()}_id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == ${entity.name.lower()}_id
@@ -0,0 +1,51 @@
<%
component_name = entity.name.capitalize() + 'List'
api_path = '/' + entity.name.lower() + 's'
%>
<template>
<div class="${entity.name.lower()}-container">
<h2>${component_name}</h2>
<el-table :data="list" border style="width: 100%">
% for field in entity.fields:
<el-table-column prop="${field['name']}" label="${field['name'].capitalize()}" width="180" />
% endfor
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { get${entity.name.capitalize()}List, delete${entity.name.capitalize()} } from '@/api/${entity.name.lower()}'
const list = ref([])
onMounted(() => {
fetchData()
})
const fetchData = async () => {
const res = await get${entity.name.capitalize()}List()
list.value = res.data
}
const handleEdit = (row) => {
// 编辑逻辑
}
const handleDelete = async (row) => {
await delete${entity.name.capitalize()}(row.id)
fetchData()
}
</script>
<style scoped>
.${entity.name.lower()}-container {
padding: 20px;
}
</style>