Inicio / Elixir / Phoenix Framework: Web en Tiempo Real / Ecto y Modelos

Ecto y Modelos

Schema, changesets, migrations, Repo y validaciones.

Principiante Bases de datos
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Ecto y Modelos en Phoenix

Ecto es la biblioteca oficial de Elixir para interactuar con bases de datos. En Phoenix, Ecto proporciona schemas para definir la estructura de los datos, changesets para validar y transformar datos, migraciones para gestionar el esquema de la base de datos y un DSL de consultas poderoso y composable.

Schemas

Los schemas definen la estructura de los datos y su mapeo a tablas de la base de datos:

defmodule MiApp.Catalogo.Producto do
  use Ecto.Schema
  import Ecto.Changeset

  schema "productos" do
    field :nombre, :string
    field :descripcion, :string
    field :precio, :decimal
    field :stock, :integer, default: 0
    field :activo, :boolean, default: true
    field :publicado_en, :naive_datetime

    belongs_to :categoria, MiApp.Catalogo.Categoria
    has_many :resenas, MiApp.Catalogo.Resena
    many_to_many :etiquetas, MiApp.Catalogo.Etiqueta, join_through: "productos_etiquetas"

    timestamps()
  end
end

Tipos de Campo

Ecto soporta una variedad de tipos de campo para representar distintos datos:

schema "ejemplo" do
  field :texto, :string
  field :numero_entero, :integer
  field :numero_decimal, :decimal
  field :flotante, :float
  field :activo, :boolean
  field :fecha, :date
  field :hora, :time
  field :fecha_hora, :naive_datetime
  field :fecha_hora_utc, :utc_datetime
  field :identificador, :binary_id       # UUID
  field :datos, :map                     # JSON/mapa
  field :lista_tags, {:array, :string}   # Array de strings
  field :estado, Ecto.Enum, values: [:borrador, :publicado, :archivado]
end

Changesets

Los changesets son el mecanismo central de Ecto para validar y transformar datos antes de insertarlos o actualizarlos:

defmodule MiApp.Catalogo.Producto do
  use Ecto.Schema
  import Ecto.Changeset

  schema "productos" do
    field :nombre, :string
    field :precio, :decimal
    field :stock, :integer
    field :email_contacto, :string
    timestamps()
  end

  def changeset(producto, attrs) do
    producto
    |> cast(attrs, [:nombre, :precio, :stock, :email_contacto])
    |> validate_required([:nombre, :precio])
    |> validate_length(:nombre, min: 3, max: 100)
    |> validate_number(:precio, greater_than: 0)
    |> validate_number(:stock, greater_than_or_equal_to: 0)
    |> validate_format(:email_contacto, ~r/@/)
    |> unique_constraint(:nombre)
  end
end

Cast y Validate

Las funciones cast y validate_* forman la base del sistema de validación:

def changeset(usuario, attrs) do
  usuario
  # cast filtra y convierte los campos permitidos
  |> cast(attrs, [:nombre, :email, :edad, :rol])
  # Validaciones disponibles
  |> validate_required([:nombre, :email])
  |> validate_length(:nombre, min: 2, max: 50)
  |> validate_format(:email, ~r/^[\w.]+@[\w.]+$/)
  |> validate_inclusion(:rol, ["admin", "usuario", "moderador"])
  |> validate_number(:edad, greater_than: 0, less_than: 150)
  |> validate_confirmation(:password, message: "las contraseñas no coinciden")
  |> unique_constraint(:email)
  |> put_change(:email, String.downcase(attrs["email"] || ""))
end

Migraciones

Las migraciones gestionan los cambios en el esquema de la base de datos de forma versionada:

# Generar una migración
# mix ecto.gen.migration crear_productos

defmodule MiApp.Repo.Migrations.CrearProductos do
  use Ecto.Migration

  def change do
    create table(:productos) do
      add :nombre, :string, null: false
      add :descripcion, :text
      add :precio, :decimal, precision: 10, scale: 2
      add :stock, :integer, default: 0
      add :activo, :boolean, default: true
      add :categoria_id, references(:categorias, on_delete: :restrict)

      timestamps()
    end

    create index(:productos, [:nombre])
    create unique_index(:productos, [:nombre])
    create index(:productos, [:categoria_id])
  end
end

# Ejecutar migraciones: mix ecto.migrate
# Revertir última migración: mix ecto.rollback

Repo CRUD

El módulo Repo proporciona las operaciones básicas para interactuar con la base de datos:

alias MiApp.Repo
alias MiApp.Catalogo.Producto

# Crear
{:ok, producto} = Repo.insert(%Producto{nombre: "Laptop", precio: 999.99})

# Leer
producto = Repo.get(Producto, 1)
producto = Repo.get!(Producto, 1)              # Lanza error si no existe
producto = Repo.get_by(Producto, nombre: "Laptop")
productos = Repo.all(Producto)

# Actualizar
changeset = Producto.changeset(producto, %{precio: 899.99})
{:ok, producto_actualizado} = Repo.update(changeset)

# Eliminar
{:ok, _producto} = Repo.delete(producto)

Queries con from, where y select

Ecto ofrece un DSL de consultas composable y seguro contra inyección SQL:

import Ecto.Query

# Consulta básica con from
query = from p in Producto,
  where: p.activo == true,
  where: p.precio > ^precio_minimo,
  order_by: [desc: p.precio],
  select: %{nombre: p.nombre, precio: p.precio}

productos = Repo.all(query)

# Consultas composables
def productos_activos do
  from p in Producto, where: p.activo == true
end

def por_categoria(query, categoria_id) do
  from p in query, where: p.categoria_id == ^categoria_id
end

def ordenar_por_precio(query, direccion \\ :asc) do
  from p in query, order_by: [{^direccion, p.precio}]
end

# Composición: combinar consultas
resultado =
  productos_activos()
  |> por_categoria(5)
  |> ordenar_por_precio(:desc)
  |> Repo.all()

Resumen

Ecto es el pilar de la capa de datos en Phoenix. Los schemas definen la estructura y relaciones de los datos, los changesets validan y transforman la entrada con cast y funciones validate_*, las migraciones gestionan el esquema de la base de datos de forma versionada, el Repo ofrece operaciones CRUD directas, y el DSL de queries con from, where y select permite construir consultas composables, seguras y expresivas.

🔒

Ejercicio práctico disponible

Schemas y Changesets de Ecto

Desbloquear ejercicios
// Schemas y Changesets de Ecto
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes