使用Facebook/Ent构建简单CMS系统的Go入门指南
ent 项目地址: https://gitcode.com/gh_mirrors/ent4/ent
前言
在当今的Web开发领域,Go语言因其简洁性和高性能而广受欢迎。然而,对于许多Go初学者来说,如何有效地处理数据库操作仍然是一个挑战。Facebook开源的Ent框架正是为解决这一问题而生,它提供了一种类型安全、高效的方式来处理Go应用程序中的数据持久化。
Ent框架简介
Ent是一个用于Go的实体框架,与传统ORM相比具有几个显著特点:
- 类型安全的Go API:完全避免使用
interface{}
和反射,提供纯Go代码实现,编辑器能理解,编译器能检查 - 图形语义数据建模:使用图形语义来建模应用数据,便于遍历复杂数据集
- 自动生成服务器代码:可生成GraphQL、gRPC或OpenAPI兼容的API层代码
- 内置Atlas集成:强大的模式管理工具,支持自动规划模式迁移
环境准备
在开始之前,请确保已安装:
- Go 1.16+
- Docker(用于运行MySQL数据库)
第一步:建立数据库模式
初始化项目
首先创建一个新的Go模块:
go mod init example.com/simple-cms
安装Ent框架:
go get -u entgo.io/ent@master
创建数据模型
使用Ent CLI初始化User和Post实体:
go run -mod=mod entgo.io/ent/cmd/ent new User Post
这会创建基本的项目结构:
ent/
├── generate.go
└── schema/
├── post.go
└── user.go
定义模式
在user.go
中定义User实体:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("email").Unique(),
field.Time("created_at").Default(time.Now),
}
}
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("posts", Post.Type),
}
}
在post.go
中定义Post实体:
func (Post) Fields() []ent.Field {
return []ent.Field{
field.String("title"),
field.Text("body"),
field.Time("created_at").Default(time.Now),
}
}
func (Post) Edges() []ent.Edge {
return []ent.Edge{
edge.From("author", User.Type).
Unique().
Ref("posts"),
}
}
生成代码
运行代码生成:
go generate ./...
这将生成大量类型安全的Go代码,用于数据库操作。
创建数据库迁移
安装Atlas工具:
curl -sSf https://atlasgo.sh | sh
生成迁移文件:
atlas migrate diff add_users_posts \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
启动MySQL容器:
docker run --rm --name entdb -d -p 3306:3306 -e MYSQL_DATABASE=ent -e MYSQL_ROOT_PASSWORD=pass mysql:8
应用迁移:
atlas migrate apply --dir file://ent/migrate/migrations \
--url mysql://root:pass@localhost:3306/ent
第二步:数据库种子数据
安装MySQL驱动:
go get -u github.com/go-sql-driver/mysql
创建main.go
并添加种子逻辑:
func seed(ctx context.Context, client *ent.Client) error {
r, err := client.User.Query().
Where(user.Name("rotemtam")).
Only(ctx)
switch {
case ent.IsNotFound(err):
r, err = client.User.Create().
SetName("rotemtam").
SetEmail("r@hello.world").
Save(ctx)
if err != nil {
return fmt.Errorf("failed creating user: %v", err)
}
case err != nil:
return fmt.Errorf("failed querying user: %v", err)
}
return client.Post.Create().
SetTitle("Hello, World!").
SetBody("This is my first post").
SetAuthor(r).
Exec(ctx)
}
运行种子程序:
go run main.go -dsn "root:pass@tcp(localhost:3306)/ent?parseTime=true"
第三步:创建首页
视图模板
创建templates/list.tmpl
:
<html>
<!-- 省略头部内容 -->
<body>
{{- range . }}
<h2>{{ .Title }}</h2>
<p>
{{ .CreatedAt.Format "2006-01-02" }} by {{ .Edges.Author.Name }}
</p>
<p>
{{ .Body }}
</p>
{{- end }}
</body>
</html>
服务器代码
定义服务器结构体:
type server struct {
client *ent.Client
}
func newServer(client *ent.Client) *server {
return &server{client: client}
}
添加首页处理器:
func (s *server) index(w http.ResponseWriter, r *http.Request) {
posts, err := s.client.Post.Query().
WithAuthor().
Order(ent.Desc(post.FieldCreatedAt)).
All(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, posts); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
路由设置
func main() {
// ...之前的代码
srv := newServer(client)
http.HandleFunc("/", srv.index)
log.Println("listening on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("http server terminated: %v", err)
}
}
总结
通过本教程,我们使用Ent框架构建了一个简单的CMS系统,涵盖了从数据模型定义、数据库迁移到Web界面开发的完整流程。Ent的类型安全特性和代码生成能力大大简化了Go应用程序的数据访问层开发,同时保持了高性能和灵活性。
对于想要进一步探索Ent的开发者,可以考虑添加以下功能:
- 用户认证系统
- 文章评论功能
- 文章分类和标签
- 分页和搜索功能
Ent框架的强大功能使得这些扩展变得简单而高效,是构建数据密集型Go应用的理想选择。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考