Saltar al contenido principal

API CRUD

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Como se mencionó en la sección de introducción, al ejecutar ent en los esquemas, se generarán los siguientes recursos:

  • Objetos Client y Tx para interactuar con el grafo.

  • Constructores CRUD para cada tipo de esquema.

  • Objeto entidad (struct de Go) para cada tipo de esquema.

  • Paquete con constantes y predicados para interactuar con los constructores.

  • Un paquete migrate para dialectos SQL. Consulta Migración para más información.

Crear un nuevo cliente

package main

import (
"context"
"log"

"entdemo/ent"

_ "github.com/mattn/go-sqlite3"
)

func main() {
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}

Crear una entidad

Guardar un usuario.

a8m, err := client.User.    // UserClient.
Create(). // User create builder.
SetName("a8m"). // Set field value.
SetNillableAge(age). // Avoid nil checks.
AddGroups(g1, g2). // Add many edges.
SetSpouse(nati). // Set unique edge.
Save(ctx) // Create and return.

GuardarX una mascota; A diferencia de Guardar, GuardarX entra en pánico si ocurre un error.

pedro := client.Pet.    // PetClient.
Create(). // Pet create builder.
SetName("pedro"). // Set field value.
SetOwner(a8m). // Set owner (unique edge).
SaveX(ctx) // Create and return.

Crear múltiples

Guardar un grupo de mascotas.

pets, err := client.Pet.CreateBulk(
client.Pet.Create().SetName("pedro").SetOwner(a8m),
client.Pet.Create().SetName("xabi").SetOwner(a8m),
client.Pet.Create().SetName("layla").SetOwner(a8m),
).Save(ctx)

names := []string{"pedro", "xabi", "layla"}
pets, err := client.Pet.MapCreateBulk(names, func(c *ent.PetCreate, i int) {
c.SetName(names[i]).SetOwner(a8m)
}).Save(ctx)

Actualizar uno

Actualizar una entidad devuelta desde la base de datos.

a8m, err = a8m.Update().    // User update builder.
RemoveGroup(g2). // Remove a specific edge.
ClearCard(). // Clear a unique edge.
SetAge(30). // Set a field value.
AddRank(10). // Increment a field value.
AppendInts([]int{1}). // Append values to a JSON array.
Save(ctx) // Save and return.

Actualizar por ID

pedro, err := client.Pet.   // PetClient.
UpdateOneID(id). // Pet update builder.
SetName("pedro"). // Set field name.
SetOwnerID(owner). // Set unique edge, using id.
Save(ctx) // Save and return.

Actualizar uno con condición

En algunos proyectos, la operación "actualizar múltiples" no está permitida y se bloquea mediante hooks. Sin embargo, existe la necesidad de actualizar una sola entidad por su ID asegurando que cumple una condición específica. En este caso, puedes usar la opción Where de la siguiente manera:

err := client.Todo.
UpdateOneID(id).
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion),
).
Exec(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("update error:", err)
}

Actualizar múltiples

Filtrar usando predicados.

n, err := client.User.          // UserClient.
Update(). // User update builder.
Where( //
user.Or( // (age >= 30 OR name = "bar")
user.AgeGT(30), //
user.Name("bar"), // AND
), //
user.HasFollowers(), // UserHasFollowers()
). //
SetName("foo"). // Set field name.
Save(ctx) // exec and return.

Consultar predicados de aristas (edges).

n, err := client.User.          // UserClient.
Update(). // User update builder.
Where( //
user.HasFriendsWith( // UserHasFriendsWith (
user.Or( // age = 20
user.Age(20), // OR
user.Age(30), // age = 30
) // )
), //
). //
SetName("a8m"). // Set field name.
Save(ctx) // exec and return.

Upsert (insertar o actualizar) uno

Ent admite operaciones upsert usando la bandera de característica sql/upsert.

err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// Use the new values that were set on create.
UpdateNewValues().
Exec(ctx)

id, err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
// Use the "age" that was set on create.
UpdateAge().
// Set a different "name" in case of conflict.
SetName("Mashraki").
ID(ctx)

// Customize the UPDATE clause.
err := client.User.
Create().
SetAge(30).
SetName("Ariel").
OnConflict().
UpdateNewValues().
// Override some of the fields with a custom update.
Update(func(u *ent.UserUpsert) {
u.SetAddress("localhost")
u.AddCount(1)
u.ClearPhone()
}).
Exec(ctx)

En PostgreSQL, se requiere el objetivo de conflicto:

// Setting the column names using the fluent API.
err := client.User.
Create().
SetName("Ariel").
OnConflictColumns(user.FieldName).
UpdateNewValues().
Exec(ctx)

// Setting the column names using the SQL API.
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictColumns(user.FieldName),
).
UpdateNewValues().
Exec(ctx)

// Setting the constraint name using the SQL API.
err := client.User.
Create().
SetName("Ariel").
OnConflict(
sql.ConflictConstraint(constraint),
).
UpdateNewValues().
Exec(ctx)

Para personalizar la sentencia ejecutada, usa la API SQL:

id, err := client.User.
Create().
OnConflict(
sql.ConflictColumns(...),
sql.ConflictWhere(...),
sql.UpdateWhere(...),
).
Update(func(u *ent.UserUpsert) {
u.SetAge(30)
u.UpdateName()
}).
ID(ctx)

// INSERT INTO "users" (...) VALUES (...) ON CONFLICT WHERE ... DO UPDATE SET ... WHERE ...
información

Dado que la API upsert se implementa mediante la cláusula ON CONFLICT (y ON DUPLICATE KEY en MySQL), Ent ejecuta solo una sentencia en la base de datos, por lo que solo se aplican los hooks de creación para estas operaciones.

Upsert múltiples

err := client.User.             // UserClient
CreateBulk(builders...). // User bulk create.
OnConflict(). // User bulk upsert.
UpdateNewValues(). // Use the values that were set on create in case of conflict.
Exec(ctx) // Execute the statement.

Consultar el grafo

Obtener todos los usuarios con seguidores.

users, err := client.User.      // UserClient.
Query(). // User query builder.
Where(user.HasFollowers()). // filter only users with followers.
All(ctx) // query and return.

Obtener todos los seguidores de un usuario específico; iniciar el recorrido desde un nodo del grafo.

users, err := a8m.
QueryFollowers().
All(ctx)

Obtener todas las mascotas de los seguidores de un usuario.

users, err := a8m.
QueryFollowers().
QueryPets().
All(ctx)

Contar el número de publicaciones sin comentarios.

n, err := client.Post.
Query().
Where(
post.Not(
post.HasComments(),
)
).
Count(ctx)

Puedes encontrar recorridos más avanzados en la siguiente sección.

Selección de campos

Obtener todos los nombres de mascotas.

names, err := client.Pet.
Query().
Select(pet.FieldName).
Strings(ctx)

Obtener todos los nombres únicos de mascotas.

names, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Strings(ctx)

Contar el número de nombres únicos de mascotas.

n, err := client.Pet.
Query().
Unique(true).
Select(pet.FieldName).
Count(ctx)

Seleccionar objetos parciales y asociaciones parciales. Obtener todas las mascotas y sus dueños, pero seleccionar y completar solo los campos ID y Name.

pets, err := client.Pet.
Query().
Select(pet.FieldName).
WithOwner(func (q *ent.UserQuery) {
q.Select(user.FieldName)
}).
All(ctx)

Escanear todos los nombres y edades de mascotas en una estructura personalizada.

var v []struct {
Age int `json:"age"`
Name string `json:"name"`
}
err := client.Pet.
Query().
Select(pet.FieldAge, pet.FieldName).
Scan(ctx, &v)
if err != nil {
log.Fatal(err)
}

Actualizar una entidad y devolver una versión parcial de la misma.

pedro, err := client.Pet.
UpdateOneID(id).
SetAge(9).
SetName("pedro").
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
Select(pet.FieldName).
Save(ctx)

Eliminar uno

Eliminar una entidad:

err := client.User.
DeleteOne(a8m).
Exec(ctx)

Eliminar por ID:

err := client.User.
DeleteOneID(id).
Exec(ctx)

Eliminar uno con condición

En algunos proyectos, la operación "eliminar múltiples" no está permitida y se bloquea mediante hooks. Sin embargo, existe la necesidad de eliminar una sola entidad por su ID asegurando que cumple una condición específica. En este caso, puedes usar la opción Where de la siguiente manera:

err := client.Todo.
DeleteOneID(id).
Where(
// Allow deleting only expired todos.
todo.ExpireLT(time.Now()),
).
Exec(ctx)
switch {
// If the entity does not meet a specific condition,
// the operation will return an "ent.NotFoundError".
case ent.IsNotFound(err):
fmt.Println("todo item was not found")
// Any other error.
case err != nil:
fmt.Println("deletion error:", err)
}

Eliminar varios

Eliminar mediante predicados:

affected, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)).
Exec(ctx)

Mutación

Each generated node type has its own type of mutation. For example, all User builders, share the same generated UserMutation object. However, all builder types implement the generic ent.Mutation interface.

Por ejemplo, para escribir código genérico que aplique un conjunto de métodos tanto en ent.UserCreate como en ent.UserUpdate, utiliza el objeto UserMutation:

func Do() {
creator := client.User.Create()
SetAgeName(creator.Mutation())
updater := client.User.UpdateOneID(id)
SetAgeName(updater.Mutation())
}

// SetAgeName sets the age and the name for any mutation.
func SetAgeName(m *ent.UserMutation) {
m.SetAge(32)
m.SetName("Ariel")
}

En algunos casos, querrás aplicar un conjunto de métodos en varios tipos. Para casos como este, puedes usar la interfaz genérica ent.Mutation, o crear tu propia interfaz.

func Do() {
creator1 := client.User.Create()
SetName(creator1.Mutation(), "a8m")

creator2 := client.Pet.Create()
SetName(creator2.Mutation(), "pedro")
}

// SetNamer wraps the 2 methods for getting
// and setting the "name" field in mutations.
type SetNamer interface {
SetName(string)
Name() (string, bool)
}

func SetName(m SetNamer, name string) {
if _, exist := m.Name(); !exist {
m.SetName(name)
}
}