Vistas
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Ent permite trabajar con vistas de bases de datos. A diferencia de los tipos regulares de Ent (esquemas), que normalmente están respaldados por tablas, las vistas
actúan como "tablas virtuales" y sus datos resultan de una consulta. Los siguientes ejemplos muestran cómo definir una VIEW
en Ent. Para más detalles sobre las distintas opciones, sigue el resto de la guía.
- Builder Definition
- Raw Definition
- External Definition
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Annotations of the CleanUser.
func (CleanUser) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.ViewFor(dialect.Postgres, func(s *sql.Selector) {
s.Select("name", "public_info").From(sql.Table("users"))
}),
}
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("public_info"),
}
}
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Annotations of the CleanUser.
func (CleanUser) Annotations() []schema.Annotation {
return []schema.Annotation{
// Alternatively, you can use raw definitions to define the view.
// But note, this definition is skipped if the ViewFor annotation
// is defined for the dialect we generated migration to (Postgres).
entsql.View(`SELECT name, public_info FROM users`),
}
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("public_info"),
}
}
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// View definition is specified in a separate file (`schema.sql`),
// and loaded using Atlas' `composite_schema` data-source.
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("public_info"),
}
}
- Las vistas son de solo lectura, por lo tanto, no se generan constructores de mutación para ellas. Si deseas definir vistas modificables (insertables/actualizables), defínelas como esquemas regulares y sigue la guía a continuación para configurar sus migraciones.
- A diferencia de
ent.Schema,ent.Viewno tiene un campoIDpredeterminado. Si quieres incluir un campoiden tu vista, debes definirlo explícitamente como campo. - No se pueden registrar hooks en vistas, ya que son de solo lectura.
- Atlas ofrece soporte nativo para vistas de Ent, tanto en migraciones versionadas como en pruebas. Sin embargo, si no estás usando Atlas y quieres utilizar vistas, necesitas gestionar sus migraciones manualmente ya que Ent no ofrece migraciones de esquema para ellas.
Introducción
Las vistas definidas en el paquete ent/schema incorporan el tipo ent.View en lugar de ent.Schema. Además de campos,
pueden tener relaciones (edges), interceptores y anotaciones para habilitar integraciones adicionales. Por ejemplo:
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
// Note, unlike real schemas (tables, defined with ent.Schema),
// the "id" field should be defined manually if needed.
field.Int("id"),
field.String("name"),
field.String("public_info"),
}
}
Una vez definida, puedes ejecutar go generate ./ent para crear los recursos necesarios para interactuar con esta vista. Por ejemplo:
client.CleanUser.Query().OnlyX(ctx)
Nota: los constructores Create/Update/Delete no se generan para ent.View.
Migración y pruebas
Tras definir el esquema de la vista, debemos informar a Ent (y a Atlas) sobre la consulta SQL que define esta vista. Si no se
configura, ejecutar una consulta de Ent, como la definida anteriormente, fallará porque no existe una tabla llamada clean_users.
El resto del documento asume que usas Ent con Atlas Pro, ya que Ent no tiene soporte de migraciones para vistas u otros objetos de base de datos además de tablas y relaciones. Sin embargo, usar Atlas o su suscripción Pro
is not mandatory. Ent no requiere un motor de migraciones específico, y mientras la vista exista en la base de datos, el cliente podrá consultarla.Para configurar nuestra definición de vista (AS SELECT ...), tenemos dos opciones:
Definirla dentro del código Go en
ent/schema.Mantener
ent/schemaindependiente de la definición de la vista y crearla externamente, ya sea manualmente o automáticamente usando Atlas.
Exploremos ambas opciones:
Definición en Go
Este ejemplo muestra cómo definir un ent.View con su definición SQL (AS ...) especificada en el esquema de Ent.
La principal ventaja de este enfoque es que la corrección del CREATE VIEW se verifica durante la migración, no durante las consultas.
Por ejemplo, si uno de los ent.Field definidos en tu ent/schema no existe en tu definición SQL, PostgreSQL
devolverá el siguiente error:
Aquí tienes un ejemplo de vista definida junto con sus campos y su consulta SELECT:
- Builder Definition
- Raw Definition
Using the entsql.ViewFor API, you can use a dialect-aware builder to define the view. Note that you can have multiple
view definitions for different dialects, and Atlas will use the one that matches the dialect of the migration.
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Annotations of the CleanUser.
func (CleanUser) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.ViewFor(dialect.Postgres, func(s *sql.Selector) {
s.Select("id", "name", "public_info").From(sql.Table("users"))
}),
}
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
// Note, unlike real schemas (tables, defined with ent.Schema),
// the "id" field should be defined manually if needed.
field.Int("id"),
field.String("name"),
field.String("public_info"),
}
}
Alternatively, you can use raw definitions to define the view. But note, this definition is skipped if the ViewFor
annotation is defined for the dialect we generated migration to (Postgres in this case).
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Annotations of the CleanUser.
func (CleanUser) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.View(`SELECT id, name, public_info FROM users`),
}
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
// Note, unlike real schemas (tables, defined with ent.Schema),
// the "id" field should be defined manually if needed.
field.Int("id"),
field.String("name"),
field.String("public_info"),
}
}
Simplifiquemos nuestra configuración creando un archivo atlas.hcl con los parámetros necesarios. Usaremos este archivo de configuración
en la sección de uso a continuación:
env "local" {
src = "ent://ent/schema"
dev = "docker://postgres/16/dev?search_path=public"
}
El ejemplo completo está en el repositorio de Ent.
Definición externa
Este ejemplo muestra cómo definir un ent.View, pero manteniendo su definición en un archivo separado (schema.sql) o
creándola manualmente en la base de datos.
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.View
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
field.Int("id"),
field.String("name"),
field.String("public_info"),
}
}
Tras definir el esquema de la vista en Ent, la definición SQL CREATE VIEW debe configurarse (o crearse) por separado para garantizar que exista en la base de datos cuando el runtime de Ent la consulte.
Para este ejemplo, usaremos la fuente de datos composite_schema de Atlas para construir un grafo de esquema a partir de nuestro paquete ent/schema y un archivo SQL que describe esta vista. Creemos un archivo llamado schema.sql y peguemos la definición de la vista en él:
-- Create "clean_users" view
CREATE VIEW "clean_users" ("id", "name", "public_info") AS SELECT id,
name,
public_info
FROM users;
A continuación, creamos un archivo de configuración atlas.hcl con un composite_schema que incluye tanto nuestro ent/schema como el archivo schema.sql:
data "composite_schema" "app" {
# Load the ent schema first with all its tables.
schema "public" {
url = "ent://ent/schema"
}
# Then, load the views defined in the schema.sql file.
schema "public" {
url = "file://schema.sql"
}
}
env "local" {
src = data.composite_schema.app.url
dev = "docker://postgres/15/dev?search_path=public"
}
El ejemplo completo se encuentra en el repositorio de Ent.
Uso
Tras configurar nuestro esquema, podemos obtener su representación usando el comando atlas schema inspect, generar migraciones, aplicarlas a una base de datos y más. Aquí tienes algunos comandos para empezar con Atlas:
Inspeccionar el esquema
El comando atlas schema inspect se usa comúnmente para inspeccionar bases de datos. Sin embargo, también podemos usarlo para examinar nuestro ent/schema e imprimir su representación SQL:
atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'
El comando anterior imprime el siguiente SQL. Observa que la vista clean_users se define en el esquema después de la tabla users:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, "public_info" character varying NOT NULL, "private_info" character varying NOT NULL, PRIMARY KEY ("id"));
-- Create "clean_users" view
CREATE VIEW "clean_users" ("id", "name", "public_info") AS SELECT id,
name,
public_info
FROM users;
Generar migraciones para el esquema
Para generar una migración para el esquema, ejecuta el siguiente comando:
atlas migrate diff \
--env local
Ten en cuenta que se crea un nuevo archivo de migración con el siguiente contenido:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, "public_info" character varying NOT NULL, "private_info" character varying NOT NULL, PRIMARY KEY ("id"));
-- Create "clean_users" view
CREATE VIEW "clean_users" ("id", "name", "public_info") AS SELECT id,
name,
public_info
FROM users;
Aplicar las migraciones
Para aplicar la migración generada a una base de datos, ejecuta el siguiente comando:
atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
En ocasiones, necesitas aplicar el esquema directamente a la base de datos sin generar un archivo de migración. Por ejemplo, al experimentar con cambios en el esquema, al levantar una base de datos para pruebas, etc. En estos casos, puedes usar el siguiente comando para aplicar el esquema directamente a la base de datos:
atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
O, al escribir pruebas, puedes usar el SDK de Go para Atlas para alinear el esquema con la base de datos antes de ejecutar las aserciones:
ac, err := atlasexec.NewClient(".", "atlas")
if err != nil {
log.Fatalf("failed to initialize client: %w", err)
}
// Automatically update the database with the desired schema.
// Another option, is to use 'migrate apply' or 'schema apply' manually.
if _, err := ac.SchemaApply(ctx, &atlasexec.SchemaApplyParams{
Env: "local",
URL: "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable",
AutoApprove: true,
}); err != nil {
log.Fatalf("failed to apply schema changes: %w", err)
}
// Run assertions.
u1 := client.User.Create().SetName("a8m").SetPrivateInfo("secret").SetPublicInfo("public").SaveX(ctx)
v1 := client.CleanUser.Query().OnlyX(ctx)
require.Equal(t, u1.ID, v1.ID)
require.Equal(t, u1.Name, v1.Name)
require.Equal(t, u1.PublicInfo, v1.PublicInfo)
Vistas actualizables e insertables
Si quieres definir una vista actualizable/insertable, configúrala como un tipo regular (ent.Schema) y añade la anotación entsql.Skip() para evitar que Ent genere la sentencia CREATE TABLE para esta vista. Luego, define la vista en la base de datos como se describe en la sección definición externa anterior.
// CleanUser represents a user without its PII field.
type CleanUser struct {
ent.Schema
}
// Annotations of the CleanUser.
func (CleanUser) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Skip(),
}
}
// Fields of the CleanUser.
func (CleanUser) Fields() []ent.Field {
return []ent.Field{
field.Int("id"),
field.String("name"),
field.String("public_info"),
}
}