mirror of
https://github.com/ent/ent.git
synced 2026-04-30 14:40:57 +03:00
226 lines
4.7 KiB
Markdown
226 lines
4.7 KiB
Markdown
---
|
|
id: generating-ent-schemas
|
|
title: Generating Schemas
|
|
---
|
|
|
|
## Introduction
|
|
|
|
To facilitate the creation of tooling that generates `ent.Schema`s programmatically, `ent` supports the manipulation of
|
|
the `schema/` directory using the `entgo.io/contrib/schemast` package.
|
|
|
|
## API
|
|
|
|
### Loading
|
|
|
|
In order to manipulate an existing schema directory we must first load it into a `schemast.Context` object:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"entgo.io/contrib/schemast"
|
|
)
|
|
|
|
func main() {
|
|
ctx, err := schemast.Load("./ent/schema")
|
|
if err != nil {
|
|
log.Fatalf("failed: %v", err)
|
|
}
|
|
if ctx.HasType("user") {
|
|
fmt.Println("schema directory contains a schema named User!")
|
|
}
|
|
}
|
|
```
|
|
|
|
### Printing
|
|
|
|
To print back out our context to a target directory, use `schemast.Print`:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"entgo.io/contrib/schemast"
|
|
)
|
|
|
|
func main() {
|
|
ctx, err := schemast.Load("./ent/schema")
|
|
if err != nil {
|
|
log.Fatalf("failed: %v", err)
|
|
}
|
|
// A no-op since we did not manipulate the Context at all.
|
|
if err := schemast.Print("./ent/schema"); err != nil {
|
|
log.Fatalf("failed: %v", err)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Mutators
|
|
|
|
To mutate the `ent/schema` directory, we can use `schemast.Mutate`, which takes a list of
|
|
`schemast.Mutator`s to apply to the context:
|
|
|
|
```go
|
|
package schemast
|
|
|
|
// Mutator changes a Context.
|
|
type Mutator interface {
|
|
Mutate(ctx *Context) error
|
|
}
|
|
```
|
|
|
|
Currently, only a single type of `schemast.Mutator` is implemented, `UpsertSchema`:
|
|
|
|
```go
|
|
package schemast
|
|
|
|
// UpsertSchema implements Mutator. UpsertSchema will add to the Context the type named
|
|
// Name if not present and rewrite the type's Fields, Edges, Indexes and Annotations methods.
|
|
type UpsertSchema struct {
|
|
Name string
|
|
Fields []ent.Field
|
|
Edges []ent.Edge
|
|
Indexes []ent.Index
|
|
Annotations []schema.Annotation
|
|
}
|
|
```
|
|
|
|
To use it:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"entgo.io/contrib/schemast"
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
func main() {
|
|
ctx, err := schemast.Load("./ent/schema")
|
|
if err != nil {
|
|
log.Fatalf("failed: %v", err)
|
|
}
|
|
mutations := []schemast.Mutator{
|
|
&schemast.UpsertSchema{
|
|
Name: "User",
|
|
Fields: []ent.Field{
|
|
field.String("name"),
|
|
},
|
|
},
|
|
&schemast.UpsertSchema{
|
|
Name: "Team",
|
|
Fields: []ent.Field{
|
|
field.String("name"),
|
|
},
|
|
},
|
|
}
|
|
err = schemast.Mutate(ctx, mutations...)
|
|
if err := ctx.Print("./ent/schema"); err != nil {
|
|
log.Fatalf("failed: %v", err)
|
|
}
|
|
}
|
|
```
|
|
|
|
After running this program, observe two new files exist in the schema directory: `user.go` and `team.go`:
|
|
|
|
```go
|
|
// user.go
|
|
package schema
|
|
|
|
import (
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{field.String("name")}
|
|
}
|
|
func (User) Edges() []ent.Edge {
|
|
return nil
|
|
}
|
|
func (User) Annotations() []schema.Annotation {
|
|
return nil
|
|
}
|
|
```
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
type Team struct {
|
|
ent.Schema
|
|
}
|
|
|
|
func (Team) Fields() []ent.Field {
|
|
return []ent.Field{field.String("name")}
|
|
}
|
|
func (Team) Edges() []ent.Edge {
|
|
return nil
|
|
}
|
|
func (Team) Annotations() []schema.Annotation {
|
|
return nil
|
|
}
|
|
```
|
|
|
|
### Working with Edges
|
|
|
|
Edges are defined in `ent` this way:
|
|
|
|
```go
|
|
edge.To("edge_name", OtherSchema.Type)
|
|
```
|
|
|
|
This syntax relies on the fact that the `OtherSchema` struct already exists when we define the edge so we can refer to
|
|
its `Type` method. When we are generating schemas programmatically, obviously we need somehow to describe the edge to
|
|
the code-generator before the type definitions exist. To do this you can do something like:
|
|
|
|
```go
|
|
type placeholder struct {
|
|
ent.Schema
|
|
}
|
|
|
|
func withType(e ent.Edge, typeName string) ent.Edge {
|
|
e.Descriptor().Type = typeName
|
|
return e
|
|
}
|
|
|
|
func newEdgeTo(edgeName, otherType string) ent.Edge {
|
|
// we pass a placeholder type to the edge constructor:
|
|
e := edge.To(edgeName, placeholder.Type)
|
|
// then we override the other type's name directly on the edge descriptor:
|
|
return withType(e, otherType)
|
|
}
|
|
```
|
|
|
|
## Examples
|
|
|
|
The `protoc-gen-ent` ([doc](https://github.com/ent/contrib/tree/master/entproto/cmd/protoc-gen-ent)) is a protoc plugin
|
|
that programmatically generates `ent.Schema`s from .proto files, it uses the `schemast` to manipulate the
|
|
target `schema` directory. To see
|
|
how, [read the source code](https://github.com/ent/contrib/blob/master/entproto/cmd/protoc-gen-ent/main.go#L34).
|
|
|
|
## Caveats
|
|
|
|
`schemast` is still experimental, APIs are subject to change in the future. In addition, a small portion of
|
|
the `ent.Field` definition API is unsupported at this point in time, to see a full list of unsupported features see
|
|
the [source code](https://github.com/ent/contrib/blob/aed7a43a3e54550c1dd9a1a066ce1236b4bae56c/schemast/field.go#L158).
|
|
|