No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Creaci贸n de modelos (tablas)

5/21
Recursos

Toda base de datos SQL posee tablas para almacenar los datos. Gracias al ORM Diesel y sus migraciones, automatizaremos la creaci贸n y actualizaci贸n de las mismas.

Primera tabla con Diesel

Comienza creando tu primera migraci贸n con el comando diesel migration generate create_posts. El mismo producir谩 un nuevo directorio dentro de /migrations que contiene dos archivos: up.sql y down.sql. En el primero, escribiremos el c贸digo SQL para generar o modificar nuestras tablas, en el segundo el c贸digo para destruir y borrar las mismas.

-- up.sql
CREATE TABLE posts (
    id SERIAL PRIMARY KEY, 
    title VARCHAR NOT NULL,
    slug VARCHAR NOT NULL,
    body TEXT NOT NULL
);
-- down.sql
DROP TABLE posts;

A continuaci贸n, utiliza el comando diesel migration run que leer谩 el archivo up.sql y crear谩 el esquema apropiado para la tabla posts. Caso contrario, si quieres destruir estas tablas, utiliza diesel migration redo.

Qu茅 son los Macros en Rust

Los Macros son similares a las funciones, con la desventaja de que su implementaci贸n es m谩s compleja porque est谩s escribiendo c贸digo Rust, que generar谩 c贸digo Rust. La definici贸n de un Macro es generalmente m谩s dif铆cil de leer, entender y mantener que una funci贸n.

Este es un concepto bastante avanzado y dif铆cil de comprender, pero es una buena oportunidad para tenerlos presente e ir averiguando m谩s de ellos m谩s adelante.

Otra importante diferencia entre los Macros y las funciones comunes es que debes definir su alcance antes de llamarlos en un archivo, al contrario de las funciones donde los defines donde quieras y los llamas cuando quieres.

Fuente: The Difference Between Macros and Functions

Los Macros son altamente poderosos, permiten manipular el core de Rust o cambiar el comportamiento de algo. Si tienes experiencia con otros lenguajes como Javascript, Python, tal vez te sirva hacer una analog铆a con el concepto de 鈥淒ecoradores鈥 para cambiar el comportamiento de una clase o variable.

Es algo complicado de entender, pero sabiendo que son similares a las funciones y cambian el comportamieto de una cosa, podemos continuar.

Configuraci贸n del proyecto

Para establecer la conexi贸n con la base de datos y realizar consultas a la tabla que hemos creado llamado posts, primero es necesario realizar varias importaciones en el archivo main.rs:

// Indicamos que vamos a utilizar macros
#[macro_use]

// Importamos Diesel
extern crate diesel;

// Importamos la conexi贸n con PostgreSQL
use diesel::prelude::*;
use diesel::pg::PgConnection;

// Importamos librerias para leer variables de entorno
use dotenv::dotenv;
use std::env;

// Importamos esquemas de BBDD y modelos
pub mod schema;
pub mod models;

fn main() {}

Tambi茅n vamos a crear una estructura que nos servir谩 como modelo de nuestra tabla posts en la base de datos y realizar el mapeo correspondiente.
Crea un archivo llamado models.rs que contendr谩 lo siguiente:

// Macro para indicar que los registros de la BBDD tendr谩n la misma forma que la estructura.
#[derive(Queryable)]        
pub struct Post {
    pub id: i32,
    pub title: String,
    pub slug: String,
    pub body: String,
}

Conexi贸n con la BBDD

Teniendo listo todas las importaciones, establezcamos la conexi贸n con la base de datos y ejecutemos la primera consulta.

// ...

fn main() {

    // Indicamos que vamos a utilizar el esquema de Posts y el modelo
    use self::models::Post;
    use self::schema::posts;
    use self::schema::posts::dsl::*;

    // Lectura de variables de entorno
    dotenv().ok();
    let db_url = env::var("DATABASE_URL").expect("La variable de entorno DATABASE_URL no existe.");

    // Conexi贸n con la BBDD
    let conn = PgConnection::establish(&db_url).expect("No se ha podido establecer la conexi贸n con la base de datos.");

    // Realizamos la consulta equivalente a: SELECT * FROM posts;
    let posts_result = posts.load::<Post>(&conn).expect("Error en la consulta SQL.");

    // Iteramos los resultados de la consulta
    for post in posts_result {
        println!("{}", post.title);
    }

}

Finalmente, utiliza cargo run para correr tu c贸digo y verificar si est谩 correctamente establecida la conexi贸n a la base y si se ejecuta la consulta. Recuerda que a煤n no tenemos registros en la tabla, as铆 que la misma no devolver谩 resultados.

Has logrado hasta aqu铆 conectarte a una base de datos SQL con Rust y realizar consultas. 隆Felicidades!


Contribuci贸n creada por: Kevin Fiorentino.

Aportes 7

Preguntas 6

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Algo que no sab铆a, y que puede ser 煤til por si tampoco lo sab铆as, es que, lo que hicimos en las siguientes l铆neas, es la manera de crear o importar un m贸dulo p煤blico en Rust (Un m贸dulo puede contener m茅todos, constructors y structs)

pub mod models;
pub mod schema;

De igual manera, en cosas como models, tenemos estructuras con variables p煤blicas, es decir, que cualquiera que lo importe, puede acceder a esas variables

pub struct Post {
    pub id: i32,
    pub title: String,
    pub slug: String,
    pub body: String,
}

Es decir, sin la palabra pub, ese campo ser铆a por defecto privado y solo el m贸dulo que contiene la estructura puede acceder a 茅l
.
Te dejo la parte de la documentaci贸n de Rust en el que hablan m谩s a detalle de esto, con un gran ejemplo 馃槂

Creo que a fecha de hoy que realizo este post, creo que hay un peque帽o cambio con diesel y es que requiere que su conexi贸n debe ser de tipo variable mutable.
Ya que cuando hacia el 鈥淪ELECT鈥 me daba un error, por lo mismo y es que load requiere una conexi贸n mutable.

Al final me quedo as铆:

let mut conn = PgConnection::establish(&database_url).expect("we didn't connect to the database");
let posts_result = posts.load::<Post>(&mut conn).expect("Error excecuting query");

Espero poder apoyar a alguien que le pueda pasar lo mismo.

Mencionar que ahora se debe importar dotenvy:

use dotenvy::dotenv;

Una breve acotaci贸n:

diesel migration revert restablece eliminando la migracion.

diesel migration redo realiza por detr谩s:
-diesel migration revert
-diesel migration run
Es decir resume los dos comandos en uno.

El tutor Hector creo que se confunde al decir que: #[derive(Queryable)] es un macro.
Realemente esto en Rust es un atributo (attribute). Los atributos son anotaciones que se colocan en el c贸digo para cambiar su comportamiento o dar informaci贸n adicional al compilador. En este caso, el atributo #[derive(Queryable)] se utiliza para generar autom谩ticamente el c贸digo necesario para que un tipo implemente la interfaz diesel::Queryable de la biblioteca Diesel de Rust, que se utiliza para convertir las filas de una base de datos en un tipo Rust.

Aqu铆 les dejo un poco mas de informaci贸n referente a los atributos en Rust por si desean tener un poco mas de informaci贸n al respecto: https://doc.rust-lang.org/reference/attributes.html

Al igual que la 鈥渕acro鈥 #[macro_use], es un atributo que se utiliza para importar macros de otros m贸dulos. Cuando se aplica este atributo a un m贸dulo, se importan las macros definidas en ese m贸dulo y se hacen disponibles para su uso en el 谩mbito de ese m贸dulo.

En Rust, las macros son funciones que toman una entrada y generan c贸digo de salida. Se usan para automatizar tareas repetitivas o para proporcionar funcionalidad que no est谩 disponible en el lenguaje. La macro #[macro_use] se utiliza com煤nmente con las macros que se proporcionan en las bibliotecas de Rust para hacer que esas macros est茅n disponibles en el 谩mbito del m贸dulo en el que se utilizan.

Por ejemplo, si una biblioteca proporciona una macro llamada my_macro, se puede importar y hacer que est茅 disponible en un m贸dulo utilizando el atributo #[macro_use] de la siguiente manera:

#[macro_use]
extern crate my_library;

fn main() {
    my_macro!();
}

En este ejemplo, la macro my_macro est谩 disponible para su uso en el 谩mbito de main porque se import贸 con #[macro_use].

Espero que esta peque帽a nota pueda ser de ayuda a quien lo necesite. #[鈥 vs println!, panic! o cualquier funcion que termine en ! no es lo mismo. Las funciones que terminan en !, son funciones macro y las anotaciones que tienen esta forma: #[鈥 son atributos.

Al parecer ahora se debe guardar la conexi贸n de la siguiente manera:

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

Macros vs. Functions

The downside to implementing a macro instead of a function is that macro definitions are more complex than function definitions because you鈥檙e writing Rust code that writes Rust code. Due to this indirection, macro definitions are generally more difficult to read, understand, and maintain than function definitions.

Another important difference between macros and functions is that you must define macros or bring them into scope before you call them in a file, as opposed to functions you can define anywhere and call anywhere.

Fuente: https://doc.rust-lang.org/book/ch19-06-macros.html#the-difference-between-macros-and-functions