Acceso a Datos con Elixir (Ecto)
Ecto es un conjunto de herramientas para el mapeo de datos y un lenguaje integrado de consulta para Elixir.
Ecto se compone de 4 componentes principales:
- Ecto.Repo: Define los repositorios que son envolturas alrededor de un almacén de datos. Usándolo, podemos insertar, eliminar y consultar un repositorio.
- Ecto.Schema: Se utilizan para asignar cualquier fuente de datos a una estructura de Elixir (mapearlos).
- Ecto.Changeset: Proporcionan una forma de filtrar, así como un mecanismo para rastrear y validar los cambios antes de que se apliquen a los datos.
- Ecto.Query: Permite realizar consultas SQL similar a Linq para recuperar información de un repositorio. Las consultas en Ecto son seguras, evitan problemas comunes como la Inyección de SQL.
Ya con las bases sobre qué es Ecto, nos vamos manos a la obra y realizamos la parte práctica.
En este momento deben tener Instalado:
- Elixir
- PostgreSQL
Para empezar, se debe crear una nueva aplicación con un supervisor, para ello usamos Mix.
$ mix new cars --sup
Esto creará un directorio con los archivos iniciales del proyecto:
--sup
crea un árbol de supervisores que mantendrá la conexión a la base de datos.
Con el proyecto ya creado ingresamos al directorio cars
con cd cars
y abrimos nuestro editor de código de preferencia, en mi caso Vs Code. Una vez abierto el proyecto abrimos el archivo mix.exs y hacemos el siguiente cambio.
defp deps do
[
{:ecto_sql, “~> 3.0”},
{:postgrex, “>= 0.14.1”}
]
end
Lo que se hizo fue agregar al proyecto las dependencias Ecto y el driver de postgres para Elixir. El paso siguiente es obtener las dependencias con:
$ mix deps.get
Repositorio (Ecto.Repo):
El paso a seguir es crear un repositorio. Debemos configurar nuestra base de datos ingresando el comando:
$ mix ecto.gen.repo -r Cars.Repo
El comando se encarga de actualizar el archivo config/config.exs
con los datos básicos para la configuración de la base de datos.
El repositorio se genera en lib/cars/repo.ex
Una nota muy importante mostrada en consola es que debemos agregar el nuevo repositorio al supervisor. Para eso editamos lib/cars/application.ex
.
Para finalizar la configuración, agregamos en config/config.exs
, la siguiente línea.
config :cars, ecto_repos: [Cars.Repo]
Ahora hemos configurado la aplicación para que pueda hacer consultas a nuestra base de datos. Posteriormente creamos una base de datos, agregamos una tabla y luego realizamos algunas consultas.
Para crear la base de datos ingresamos en la consola el comando
$ mix ecto.create
Ya creada la base de datos el paso siguiente es crear las tablas que consultaremos.
Migraciones (Ecto.Migration)
Para crear migraciones se usa el siguiente comando:
$ mix ecto.gen.migration brand
$ mix ecto.gen.migration car
Este comando generará un nuevo archivo de migración en la ruta priv/repo/migrations
, que está vacío de forma predeterminada, entonces como ingresamos dos comandos tenemos dos archivos, brand y car:
Para ejecutar las migraciones y crear las tablas brand
y car
ejecutaremos el siguiente comando:
$ mix ecto.migrate
Ya con eso las tablas car y brand son creadas por Ecto en nuestra base de datos.
Esquema (Ecto.schema)
El esquema es una representación de Elixir de los datos de nuestra base de datos. Los esquemas se asocian comúnmente con una tabla de base de datos, sin embargo, también pueden asociarse con una vista de base de datos.
Vamos a crear el esquema dentro de la aplicación en lib/cars/car.ex
y se debe hacer lo mismo para brand:
Esto define el esquema de la base de datos a la que se asigna. En este caso, le estamos diciendo a Ecto que el esquema Cars.car
se asigna a la tabla car
en la base de datos.
Ya tenemos a disposición el esquema, si ingresamos a una sesión de iex con iex -S mix
podemos hacer lo siguiente:
$ brand = %Cars.Brand{}
Con esto el sistema nos devuelve la estructura de brand, pero también podemos setear valores
$ brand = %Cars.Brand{description: “Chevrolet”}
También es posible insertar un nuevo registro en la base de datos
$ Cars.Repo.insert(brand)
Ya Ecto se encargó de realizar el insert donde se asigna el id 1 a Chevrolet, como vemos una inserción exitosa nos retorna una tupla. Hacemos lo mismo para car.
Es posible validar los cambios antes de guardarlos en la base de datos, por ejemplo podemos requerir que todos los campos sean obligatorios, para eso está Ecto.Changeset
, vamos a validar que todos los campos de car sean obligatorios, para lograrlo ingresamos a nuestro esquema car y agregamos:
Lo que hace es que toma un car
, la primera función se encarga de decirle al changeset que parámetros se pueden usar; la siguiente línea donde llamamos validate_required
indica que valores son requeridos. Probamos el changeset:
El changeset nos informa que model
no puede estar vacía.
También es posible hacer múltiples insert de la siguiente manera:
Ahora que tenemos la tabla poblada hagamos consultas:
Para obtener un solo registro de la tabla:
$ Cars.Car |> Ecto.Query.first
Ese comando nos crearía un Ecto.Query pero si queremos ejecutar la consulta:
$ Cars.Car |> Ecto.Query.first |> Cars.Repo.one
Si queremos retornar todos los registros:
$ Cars.Car |> Cars.Repo.all
Si queremos obtener un registro por ID:
$ Cars.Car |> Cars.Repo.get(1)
Si queremos un registro basado en un atributo específico usamos:
$ Cars.Car |> Cars.Repo.get_by(brand_id: 2)
Y filtramos resultados con:
$ Cars.Car |> Ecto.Query.where(model: 2018) |> Cars.Repo.all
Para Actualizar registros requerimos que primero se obtenga un registro:
$ car = Cars.Car |> Ecto.Query.first |> Cars.Repo.one
Una vez se obtiene el registro, construimos un changeset, así Ecto sabe qué esta cambiando
$ changeset = Cars.Car.changeset(car, %{model: 2017})
y para informar a la db el cambio ejecutamos:
$ Cars.Repo.update(changeset)
Con eso se logró Actualizar el registro, es el mismo procedimiento para realizar un delete, se obtiene el registro y luego se realiza
$ Cars.Repo.delete(car)
Con esto se ha revisado parte de lo que podemos hacer con Ecto en Elixir. Falta mucho más por aprender y lo podemos revisar en la documentación de Ecto. En las próximas publicaciones avanzaremos más con Elixir.
El código se encuentra en el siguiente repositorio