Modelo Post con Diesel y PostgreSQL

Resumen

Crear modelos en Rust con Diesel es el primer paso para estructurar la información que tu aplicación va a manejar. Aquí aprendes a definir el modelo Post, generar migraciones SQL y ejecutar queries básicas contra PostgreSQL, ideal si vienes del curso anterior de conexión a base de datos.

Por qué empezar por el modelo de blog post

Antes de mostrar contenido en pantalla necesitas un esqueleto que describa cómo se comporta la información. Ese esqueleto es el modelo, y en este caso representa cada blog post que vas a guardar en la base de datos.

La idea es sencilla: defines la tabla en SQL, defines su contraparte en Rust como una struct, y Diesel se encarga de traducir entre ambos mundos. Así separas la lógica del código de los datos sensibles y mantienes todo ordenado.

¿Qué es un modelo en Diesel? Es una estructura de Rust que representa una fila de tu tabla en la base de datos. Diesel la convierte automáticamente en filas SQL gracias al macro Queryable.

Cómo escribir la migración SQL para crear la tabla posts

Cada migración en Diesel tiene dos archivos: uno que crea y otro que destruye. Esto te permite avanzar o retroceder entre versiones de la base de datos sin romper nada [01:05].

En up.sql defines la creación de la tabla posts con estos campos:

  • id de tipo serial primary key, que genera identificadores únicos automáticamente.
  • title de tipo varchar not null, para el título del post.
  • slug de tipo varchar not null, que permite acceder al post desde la URL.
  • body de tipo text, que guarda el contenido completo.

En down.sql solo necesitas una línea: drop table posts. Como dice la clase, es más fácil destruir que crear [01:55].

Cómo ejecutar y revertir migraciones con Diesel CLI

Desde la terminal corres diesel migration run para aplicar la migración y crear la tabla. Si te equivocaste, diesel migration redo regresa al estado anterior y vuelve a aplicarla [02:15].

Al ejecutar la migración, Diesel genera automáticamente un archivo schema.rs con un macro que describe la tabla. Los macros en Rust son como superfunciones capaces de modificar el comportamiento del lenguaje, y aquí se usan para mapear la tabla SQL a código Rust [02:35].

Cómo conectarse a PostgreSQL desde main.rs

En main.rs empiezas cargando las variables de entorno con dotenv().ok(), lo que lee el archivo .env sin tirar errores si algo falta. Luego importas std::env para acceder a la variable DATABASE_URL.

rust let db_url = env::var("DATABASE_URL") .expect("DB URL variable no encontrada");

El uso de expect lanza un mensaje claro si la variable no existe. Nunca imprimas la URL real en producción: la clase lo hace solo para verificar que la carga funciona [04:10].

Para la conexión necesitas tres importaciones clave:

  • use diesel::pg::PgConnection, que maneja la conexión con PostgreSQL.
  • use diesel::prelude::*, que trae las funciones más usadas de Diesel.
  • #[macro_use] extern crate diesel, que habilita el uso intensivo de macros del crate.

La conexión se establece así:

rust let conn = PgConnection::establish(&db_url) .expect("No nos pudimos conectar a la base de datos");

¿Para qué sirve expect en Rust? Es un atajo para manejar Result: si la operación funciona, devuelve el valor; si falla, detiene el programa con el mensaje que tú definas.

Cómo definir la struct Post como modelo Queryable

Creas un archivo models.rs y dentro defines la estructura que representa un post. Todos los campos van como pub para que sean accesibles desde otros módulos:

rust #[derive(Queryable)] pub struct Post { pub id: i32, pub title: String, pub slug: String, pub body: String, }

El macro #[derive(Queryable)] es el que hace la magia: indica a Diesel que cada fila devuelta por una consulta SQL puede convertirse en una instancia de Post [06:30]. Sin este derive, Rust no sabría cómo mapear los resultados.

Fíjate en el detalle: los campos van separados por comas, no por punto y coma, porque estás dentro de una struct, no de un bloque de instrucciones.

Cómo ejecutar tu primera query con Diesel

En main.rs declaras los módulos del proyecto y traes el esquema y el modelo al alcance:

rust pub mod schema; pub mod models;

use self::schema::posts::dsl::*; use self::models::Post;

El dsl::* importa el lenguaje específico de dominio que Diesel construye sobre tu tabla. Con eso ya puedes hacer el equivalente a SELECT * FROM posts con una sola línea:

rust let posts_result = posts .load::<Post>(&conn) .expect("Error al ejecutar la query");

for post in posts_result { println!("{}", post.title); }

El método load::<Post> le dice a Diesel: trae las filas y conviértelas en instancias de Post. Si la tabla está vacía, el bucle simplemente no imprime nada, que es lo que ocurre en la primera ejecución [08:40].

Errores comunes al importar módulos en Rust

Durante la clase aparecen errores típicos al organizar los use. La consola se queja porque no encuentra Post o models en el scope. La solución pasa por dos cosas:

  • Declarar los módulos con pub mod models; y pub mod schema; en main.rs.
  • Usar self:: en lugar de super:: cuando importas desde el mismo nivel.

Esta parte es donde más se traba la mayoría al empezar con Rust, porque el sistema de módulos es estricto y obliga a ser explícito con cada ruta.

¿Te quedó alguna duda sobre cómo conectar tu modelo con la tabla? Cuéntame en los comentarios qué campo agregarías a tu propio modelo de blog post.