first commit
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user