Content Types
完整实战 — 在线课程平台
用内容类型构建一个完整的多表在线课程平台,包含讲师、课程和课时。
本实战构建一个真实的多表系统:在线课程平台,包含三个关联的内容类型 — 讲师、课程和课时。
数据模型
Instructor(讲师)──< Course(课程)>── Tag(标签)
│
└──< Lesson(课时)- 一个讲师有很多课程(
one_to_many) - 一门课程属于一个讲师(
many_to_one) - 一门课程有很多课时(
one_to_many) - 一门课程通过中间表关联多个标签(
many_to_many) - 一个课时属于一门课程(
many_to_one)
1. 讲师
创建 extensions/content_types/instructor.toml:
[content_type]
name = "Instructor"
singular = "instructor"
plural = "instructors"
table = "instructors"
[[fields]]
name = "name"
field_type = "text"
required = true
[[fields]]
name = "bio"
field_type = "richtext"
[[fields]]
name = "avatar"
field_type = "media"
media_config = { accept = ["image/*"], max_count = 1 }
[[fields]]
name = "email"
field_type = "email"
required = true
unique = true
[[fields]]
name = "website"
field_type = "text"
[[fields]]
name = "courses"
field_type = "relation"
relation = { relation_type = "one_to_many", target = "courses" }
[content_type.implements]
protocols = ["timestampable"]2. 课程
创建 extensions/content_types/course.toml:
[content_type]
name = "Course"
singular = "course"
plural = "courses"
table = "courses"
[[fields]]
name = "title"
field_type = "text"
required = true
max_length = 200
[[fields]]
name = "slug"
field_type = "uid"
target_field = "title"
[[fields]]
name = "cover"
field_type = "media"
media_config = { accept = ["image/*"], max_count = 1 }
[[fields]]
name = "description"
field_type = "text"
max_length = 500
[[fields]]
name = "price"
field_type = "decimal"
min = 0
[[fields]]
name = "level"
field_type = "enum"
enum_values = ["beginner", "intermediate", "advanced"]
default = "beginner"
[[fields]]
name = "instructor"
field_type = "relation"
relation = { relation_type = "many_to_one", target = "instructors" }
[[fields]]
name = "lessons"
field_type = "relation"
relation = { relation_type = "one_to_many", target = "lessons" }
[[fields]]
name = "tags"
field_type = "relation"
relation = { relation_type = "many_to_many", target = "tags", through = "courses_tags" }
[[fields]]
name = "status"
field_type = "enum"
enum_values = ["draft", "published", "archived"]
default = "draft"
[[indexes]]
fields = ["slug"]
unique = true
[content_type.implements]
protocols = ["timestampable", "ownable", "sortable"]
[[content_type.implements.protocols]]
name = "statusable"
values = "draft,published,archived"
default = "draft"
[content_type.api.list]
access = "public"
filter = 'status = "published"'
fields = ["title", "slug", "cover", "price", "level", "instructor"]
[content_type.api.create]
access = "admin"
[content_type.api.update]
access = "admin"
[content_type.api.delete]
access = "admin"3. 课时
创建 extensions/content_types/lesson.toml:
[content_type]
name = "Lesson"
singular = "lesson"
plural = "lessons"
table = "lessons"
[[fields]]
name = "title"
field_type = "text"
required = true
[[fields]]
name = "content"
field_type = "richtext"
[[fields]]
name = "video_url"
field_type = "text"
[[fields]]
name = "duration_minutes"
field_type = "integer"
min = 1
[[fields]]
name = "is_free"
field_type = "boolean"
default = false
[[fields]]
name = "course"
field_type = "relation"
relation = { relation_type = "many_to_one", target = "courses" }
[content_type.implements]
protocols = ["timestampable", "sortable", "nestable"]4. 标签(可选)
创建 extensions/content_types/tag.toml:
[content_type]
name = "Tag"
singular = "tag"
plural = "tags"
table = "course_tags"
[[fields]]
name = "name"
field_type = "text"
required = true
unique = true
[content_type.implements]
protocols = ["timestampable"]结果
启动服务器。三张表自动创建,完整的 CRUD API 和跨表关联解析就绪。
列出课程(填充讲师和标签)
curl http://localhost:9898/api/v1/cms/courses?include=instructor,tags{
"items": [
{
"id": "abc123",
"title": "Rust 入门到实战",
"slug": "rust-ru-men-dao-shi-zhan",
"price": "29.99",
"level": "beginner",
"status": "published",
"instructor": {
"id": "inst1",
"name": "Alice Chen",
"email": "alice@example.com"
},
"tags": [
{ "id": "t1", "name": "Rust" },
{ "id": "t2", "name": "系统编程" }
]
}
],
"total": 1,
"page": 1,
"page_size": 20
}获取课程详情(填充课时和讲师)
curl http://localhost:9898/api/v1/cms/courses/abc123?include=lessons,instructor按课程过滤课时
curl "http://localhost:9898/api/v1/cms/lessons?course_id=abc123&sort=sort_key:asc"创建课程(仅管理员)
curl -X POST http://localhost:9898/api/v1/cms/courses \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Rust 入门到实战",
"price": 29.99,
"level": "beginner",
"instructor": "inst1",
"tags": ["t1", "t2"],
"status": "published"
}'创建课时
curl -X POST http://localhost:9898/api/v1/cms/lessons \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Hello World",
"content": "<p>你的第一个 Rust 程序</p>",
"duration_minutes": 15,
"is_free": true,
"course": "abc123"
}'更新课时
curl -X PUT http://localhost:9898/api/v1/cms/lessons/{id} \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"duration_minutes": 20}'搜索课程
curl "http://localhost:9898/api/v1/cms/courses?search=rust&include=instructor"你获得了什么
| 功能 | 来源 |
|---|---|
| 3 张数据库表 | TOML 自动迁移 |
| 15+ REST 端点 | 自动生成路由 |
| Slug 自动生成 | uid 字段类型 |
| 所有记录自动时间戳 | timestampable 协议 |
| 作者追踪 | ownable 协议 |
| 默认排序 | sortable 协议 |
| 课时树形结构 | nestable 协议 |
| 公开仅显示已发布 | statusable + 规则引擎 |
| 仅管理员可写入 | 访问控制 |
| 跨表填充 | include 参数 |
| 标签中间表 | many_to_many 自动创建 |
| Slug 唯一约束 | 索引定义 |
零行 Rust 代码,零条手写 SQL。
