Está en la página 1de 68

Clase 39 - Relaciones y CRUD completo

Requisitos previos

¿No sé si ya tienes eso? ¿quién está compartiendo? ¿Guillermo?


Ahí estoy instalando las dependencias.

Ahí se están instalando, hasta que se termine, seguí leyendo si puedes, María. Okay.
Micro desafío - Paso 1

Cuando termines eso, ya podríamos empezar a crear las asociaciones. O sea relaciones
aquí, pero serían asociaciones en sequelize. A ver, no sé si están hechos aquí. No, no
están hechos.

Una película tiene un género.


Vamos al modelo Movie.js.

Tienes que hacer Movie.associate = a una función. Entre paréntesis ponemos models. Y
abro las llaves. Y dentro de las llaves, iría Movie.belongsTo y ahí iría entre paréntesis
models.Genre. Y después abro otra llave y ahí le pondríamos el as: Abro comillas y pongo
el nombre de la relación. El nombre de las relaciones, a pesar de que la teoría nos dice que
escribamos solo una palabra, así como géneros, puedes poner por ejemplo
movie_tiene_un_Genre.
Bajo ese nombre de relación, se va a guardar cuando queramos consultar el género que
nos devuelva.
Y después de eso sería la foreign key. Que sería el id de géneros, que creo que es
genre_id, me parece. Y creo que ahí está el primer punto.

module.exports = (sequelize, dataTypes) => {


let alias = 'Movie'; // esto debería estar en singular
let cols = {
id: {
},
title: {
},
rating: {
},
awards: {
},
release_date: {
},
length: dataTypes.BIGINT(10),
genre_id: dataTypes.BIGINT(10)
};
let config = {
}
const Movie = sequelize.define(alias,cols,config);

Movie.associate = function (models) {


Movie.belongsTo(models.Genre, { // models.Genre -> Genres es el valor de alias
en genres.js
as: "genre",
foreignKey: "genre_id"
})
}

return Movie
};
Vamos al modelo Genre.js.
Y el siguiente requisito es que un Genre tiene muchas películas.

Un género tiene muchas películas.

Vamos al modelo Genre.js

Entonces va a ser muy parecida la estructura, pero va a ser con asMany. Genre.asMany y
ahí iría entre paréntesis models.Movie.
as: Abro comillas y pongo el nombre de la relación: el as ya va a ser de movies, y la
foreign key se mantiene.
// const { TINYINT, INTEGER } = require("sequelize/types");

module.exports = (sequelize, dataTypes) => {


let alias = 'Genre';
let cols = {
id: {
type: dataTypes.BIGINT(10).UNSIGNED,
primaryKey: true,
allowNull: false,
autoIncrement: true
},
// created_at: dataTypes.TIMESTAMP,
// updated_at: dataTypes.TIMESTAMP,
name: {
type: dataTypes.STRING(100),
allowNull: false
},
ranking: {
type: dataTypes.BIGINT(10).UNSIGNED,
allowNull: false
},
active: {
type: dataTypes.BOOLEAN,
allowNull: false
}
};
let config = {
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: false
}
const Genre = sequelize.define(alias, cols, config);

Genre.associate = function(models) {
Genre.hasMany(models.Movie, { // models.Movies -> Movie es el valor de alias en
movie.js
as: "movies", // El nombre del modelo pero en plural
foreignKey: "genre_id"
})
}

return Genre
};
¿Y qué otra relación más pide? Después pide la de película con actor, que es n:m,
entonces cambia un poquitito.

Una película tiene muchos actores.

Vamos al modelo película.


Ya tenemos la asociación del modelo Movie.js con el modelo Genre.js.
Ahora tenemos que hacer la asociación de Movie.js con Actor.js.
Y ahí tenemos que hacer un belongsToMany de Actor. El as y el foreign key siguen
estando como antes. En este caso, ¿Cuál sería la foreign key? Y tenemos que pensar que
aquí tenemos una tabla intermedia, una tabla pivot que es actor-películas.
Ese es otro campo que tenemos que agregar, que sería el through. Ahí tengo que agregar
el nombre de la tabla pivot y sería actor-movie.
Una pregunta ¿El orden en el que ponemos as, through, foreign key, influye? ¿O
podemos ponerlo en cualquier orden?
No habría problema.
¿Y la foreign key cuál sería? Estamos en película. Yo creo que actor_id sería la
otherKey, eso va después. Y después va el timestamps = false.
module.exports = (sequelize, dataTypes) => {
let alias = 'Movie'; // esto debería estar en singular
let cols = {
id: {
},
title: {
},
rating: {
},
awards: {
},
release_date: {
},
length: dataTypes.BIGINT(10),
genre_id: dataTypes.BIGINT(10)
};
let config = {
}
const Movie = sequelize.define(alias,cols,config);

Movie.associate = function (models) {


Movie.belongsTo(models.Genre, { // models.Genre -> Genres es el valor de alias en
genres.js
as: "genre",
foreignKey: "genre_id"
})

Movie.belongsToMany(models.Actor, { // models.Actor -> Actors es el valor de


alias en actor.js
as: "actors",
through: 'actor_movie',
foreignKey: 'movie_id',
otherKey: 'actor_id',
timestamps: false
})

return Movie
};
Vamos al modelo Actor.js

Y lo mismo para el modelo Actor. Lo mismo, pero al revés.


Copio lo que hicimos en Movie.js

Y lo pego en el modelo Actor.js y comienzo a modificar.


Cambio Movie

Por Actor.

Borro el Movie.belogsTo Genre.


Reemplazo Movie

Por Actor.

Y Actor por Movie.

Reemplazo la foreignKey de movie_id.

a actor_id

Y otherKey de actor_id a movie_id.

Tienen el mismo nombre de asociación que la relación Movie con Actor, hay que cambiarlo
La relación de la tabla Actor con Movie, como estoy parado en la tabla Actor, debería
llamarse movies, para hacer referencia a la tabla con la que se relaciona.

Ahí quedaría el modelo Actor.js.

module.exports = (sequelize, dataTypes) => {


let alias = 'Actor';
let cols = {
id: {
type: dataTypes.BIGINT(10).UNSIGNED,
primaryKey: true,
autoIncrement: true
},
// created_at: dataTypes.TIMESTAMP,
// updated_at: dataTypes.TIMESTAMP,
first_name: {
type: dataTypes.STRING(100),
allowNull: false
},
last_name: {
type: dataTypes.STRING(100),
allowNull: false
},
rating: {
type: dataTypes.DECIMAL(3,1),
allowNull: false
},
favorite_movie_id: dataTypes.BIGINT(10).UNSIGNED
};

let config = {
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: false
}
const Actor = sequelize.define(alias, cols, config);

Actor.associate = function (models) {


Actor.belongsToMany(models.Movie, { // models.Movie -> Movies es el valor de
alias en movie.js
as: "movies",
through: 'actor_movie',
foreignKey: 'actor_id',
otherKey: 'movie_id',
timestamps: false
})
}

return Actor
};
Micro desafío - Paso 2

Creamos el CRUD /movies/add (GET)

Lo primero sería habilitar la ruta.

Vamos a routes/moviesRoutes.js.

const express = require('express');


const router = express.Router();
const moviesController = require('../controllers/moviesController');

router.get('/movies', moviesController.list);
router.get('/movies/new', moviesController.new);
router.get('/movies/recommended', moviesController.recomended);
router.get('/movies/detail/:id', moviesController.detail);
//Rutas exigidas para la creación del CRUD
router.get('/movies/add', moviesController.add);
// router.post('/movies/create', moviesController.create);
// router.get('/movies/edit/:id', moviesController.edit);
// router.put('/movies/update/:id', moviesController.update);
// router.get('/movies/delete/:id', moviesController.delete);
// router.delete('/movies/delete/:id', moviesController.destroy);

module.exports = router;
Esta ruta nos llevaría al formulario de creación de una película.

Vamos a controllers/moviesController.js

Aquí ya están para utilizar cada uno de los modelos, para llamarlos con ese nombre
directamente.

y acá está con desconstructing


Vamos a hacer la magia en la función add

¿Qué tiene que hacer esto? Mostrar el formulario de creación de la película, movieAdd.
De la carpeta View, moviesAdd.ejs.

Las vistas ya vienen hechas.


const path = require('path');
const db = require('../database/models');
const sequelize = db.sequelize;
const { Op } = require("sequelize");

//Aqui tienen una forma de llamar a cada uno de los modelos


// const {Movies,Genres,Actor} =
require('../database/models');

//Aquí tienen otra forma de llamar a los modelos creados


const Movies = db.Movie;
const Genres = db.Genre;
const Actors = db.Actor;

const moviesController = {
'list': (req, res) => {
db.Movie.findAll()
.then(movies => {
res.render('moviesList.ejs', {movies})
})
},
'detail': (req, res) => {
db.Movie.findByPk(req.params.id)
.then(movie => {
res.render('moviesDetail.ejs', {movie});
});
},
'new': (req, res) => {
db.Movie.findAll({
order : [
['release_date', 'DESC']
],
limit: 5
})
.then(movies => {
res.render('newestMovies', {movies});
});
},
'recomended': (req, res) => {
db.Movie.findAll({
where: {
rating: {[db.Sequelize.Op.gte] : 8}
},
order: [
['rating', 'DESC']
]
})
.then(movies => {
res.render('recommendedMovies.ejs', {movies});
});
},
//Aqui dispongo las rutas para trabajar con el CRUD
add: function (req, res) {

},
create: function (req,res) {

},
edit: function(req,res) {

},
update: function (req,res) {
},
delete: function (req,res) {

},
destroy: function (req,res) {

}
}

module.exports = moviesController;

Agregamos un botón de Agregar en detalle de películas

Vamos a ver si tiene todo eso.

Y ahí tenemos que agregar un botón de Agregar y uno de listado de películas.


¿Y por qué no lo copias y lo pegas otro del botón que está en movieAdd.ejs?
Copiamos el botón.

Y lo pegamos en moviesDetail.ejs.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title><%= movie.title %> </title>
<link rel='stylesheet' href='/css/style.css' />

</head>
<body>
<figure>
<img src="/img/logo-DH.png" alt="Logo Digital House">
</figure>
<h1><%= movie.title %> </h1>
<br>
<p>CALIFICACIÓN: <%= movie.rating %> </p>
<p>PREMIOS: <%= movie.awards %> </p>
<p>DURACIÓN: <%= movie.length + ' min'%> </p>
<p>FECHA DE CREACIÓN: <%= movie.release_date %> </p>
<br>
<section>
<a class="botonModificar" href="/movies/edit/<%=
movie.id %>">Modificar</a>
</section>

</body>
</html>

A ver vamos viendo cómo va quedando

Vamos a Avatar.
Ahí está el botón agregar.
¿Y qué hace este botón? El botón Agregar envía a esta URL: /movies/add (GET).
Y un botón de listado, que conduzca la ruta /movies.
Este último botón, lo podemos copiar de moviesAdd.ejs.

<a class="botonVolver" href="/movies">Listado


de Películas</a></p>

Y lo pegamos en moviesDetail.ejs.
Actualizamos moviesDetail.ejs.

Y ya me aparece el nuevo botón.


El botón de Listado de películas si funciona, pero el botón de Agregar ¿Cómo lo hacen
funcionar? El de agregar tiene que enviar a la URL /movies/add (GET). A través de un
form. Agregamos un form y lo ponemos al botón dentro.

Actualizo, pero el botón se pone encima del otro


Pero para solucionarlo por ahora ¿No era más sencillo agregar una a (address)
simplemente, que vaya a /movies/add, con la clase que estaba? Esa nos llevaría
solamente al formulario, a la vista con un formulario.
Todavía no hemos llegado a la vista, porque le tenemos que mandar algo más.

Este es un mini formulario con el botón que nos manda esta dirección (/movies/add), pero
si hago clic en agregar, la dirección nos sale este error:
Porque acá está tratando de cargar algo que se llama AllGenres. Este es un objeto, que le
viene, que debería ser para añadir los géneros. Porque si no recuerdo mal, pide que tenga
un Select

Hacemos un <select> que permita elegir el género de la película

que esto ya lo tiene, que permita elegir el género de cada película.

Vamos al controllers/moviesController.js para hacer la magia en la función add

En la función add,

add: function (req, res) {


res.render('moviesAdd');
},

Además de mostrar movieAdd, se tiene que mandar algo.

add: function (req, res) {


res.render('moviesAdd', { algo });
},

Pero ese algo son los géneros. ¿Y cómo obtenemos las películas? Utilizando un findAll.
¿Cómo lo quieren hacer? ¿Con el async o las promesas? Creo que habíamos dicho con
async.

add: async function (req, res) {


res.render('moviesAdd', { algo });
},

También tenemos que usar Try Catch

add: async function (req, res) {


try {

} catch (error) {

}
res.render('moviesAdd', { algo });
},

Lo ponemos al res.render dentro del Try.

add: async function (req, res) {


try {
res.render('moviesAdd', { algo });
} catch (error) {

}
},

¿Ahí se puede usar un findOne en vez de un findAll? ¿Se podría usar, si le pasamos por
get el id o algo de una película?
No, porque en este caso, lo que quiere mostrar, es el formulario para agregar película, y se
tiene que mostrar un select que me muestre todos los géneros para seleccionar.

19:23
Volviendo a moviesController.js, Aquí podemos crear una constante que se va a llamar
exactamente igual que el objeto que recibe (allGenres),

add: async function (req, res) {


try {
const allGenres =
res.render('moviesAdd', { algo });
} catch (error) {

}
},

Acá no necesito hacer el db.Genre, eso es lo que hacemos siempre, porque arriba ya está
en la variable Genres.

¿Esto debería estar dentro de la función asíncrona? No debería estar acá, ¿o sí? ¿no hay
problema profe con que esté acá fuera? ¿Se lo puede usar igual? ¿Eso debería estar
definido dentro de el try catch?
No hay problema, porque en realidad todavía no está haciendo ninguna promesa.
Solamente estamos obteniendo de db, los modelos movie, Genre y Actor pero todavía no
estamos ejecutando ningún método como findAll, o algo que tenga una espera.
Igual ahí, Guille, en el add, ¿no vas a mostrar la vista del formulario?
¿En el add? Sí. Creo que en el add va a mostrar y en el POST.
Por eso. ¿Porqué no mostrás directamente en el vista? Porque la vista necesita recibir el
allGenres para cargar el option con todos los nombres de género de las películas.
Exactamente. Porque ahí está iterando, para mostrar todas las opciones.
Entonces, como dice el profe, directamente acá (en la variable Genres) ya está cargado y
no estamos haciendo ninguna promesa, así que voy a utilizar este

Yo necesito obtener todos los géneros. Así que para eso acá voy a utilizar Genres.findAll().
Le agregamos un await antes y un async antes de function. Eso me devolvería todos los
géneros del modelo de géneros.

add: async function (req, res) {


try {
const allGenres = await Genres.findAll();

res.render('moviesAdd', { algo });


} catch (error) {

}
},
Entonces ahora, a allGenres lo mando a la vista y en el catch le agrego lo que pongo en
los demás: res.status(500).send(error)

add: async function (req, res) {


try {
const allGenres = await Genres.findAll();

res.render('moviesAdd', { algo });


} catch (error) {
res.status(500).send(error)
}
},

Entonces ahí vamos a ver si funciona. La función add trabaja de forma asíncrona, con el
async y el await, y esto sería la promesa

que en este caso está buscando todos los géneros, los guarda en la variable allGenres y
los manda a la vista moviesAdd. Entonces yo había entrado a la película Avatar
Hago clic en Agregar. Me lleva a

Claro pero el agregar ¿Por qué está? El agregar ya está ubicado en el listado de películas.
No iba ahí el botón, vamos a controllers/moviesDetail.js.
Este no iba aquí, claro, acá van otros botones, los de modificar y borrar. Por eso me parecía
raro que estés haciendo el de agregar ahí. Pero el enunciado es un poco confuso ahí,
porque en la vista detalle de películas, un botón de agregar que envíe a esta url. ¿Cuál
sería la interpretación profe?
En realidad, está mal la consigna, porque ya tenían un botón existente, pero estaba en otra
vista.
Bueno, ha servido de repaso.
Pero el botón Agregar una película si funciona, o sea se está cargando bien,
Si hacemos clic en el botón Agregar una película,
Me lleva a agregar película

A ver, vamos a ver cómo está implementado ese botón. Ese botón debería estar en la vista
moviesList.ejs.
Ahí está, como dijo el profe que hagamos. Entonces así está implementado acá, en
moviesList.ejs. y vamos a moviesAdd.ejs.
El tema es que ahora si se carga la vista, está título, Rating y esto es lo que hicimos
pidiendo los géneros, para que se muestren todas las opciones:

Ya estaría terminado el punto 1.

Hacemos la magia en la función create, para agregar una película

Entonces estamos en la vista moviesAdd.ejs. Esto es lo que recibimos.


Y vamos a routes/moviesRoutes.js y habilitamos esta ruta:

const express = require('express');


const router = express.Router();
const moviesController =
require('../controllers/moviesController');

router.get('/movies', moviesController.list);
router.get('/movies/new', moviesController.new);
router.get('/movies/recommended',
moviesController.recomended);
router.get('/movies/detail/:id', moviesController.detail);
//Rutas exigidas para la creación del CRUD
router.get('/movies/add', moviesController.add);
router.post('/movies/create', moviesController.create);
// router.get('/movies/edit/:id', moviesController.edit);
// router.put('/movies/update/:id', moviesController.update);
// router.get('/movies/delete/:id', moviesController.delete);
// router.delete('/movies/delete/:id',
moviesController.destroy);
module.exports = router;

Y directamente vamos a controllers/moviesController.js, a la función create.


Acá en create, se tiene que crear la lógica para que se guarde una nueva película.
Para ahorrarnos tiempo, vamos a fijarnos si tengo un create en otro proyecto. Acá encontré
uno.
Lo pegamos en nuestro proyecto

create: function (req, res) {


db.Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
.then(()=> {
res.redirect('/movies');
})
},

En este caso, no estamos utilizando async, así que le agregamos el async antes de la
función para transformarla en asincrónica.

create: async function (req, res) {


db.Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
.then(()=> {
res.redirect('/movies');
})
},

Le agregamos el Try Catch

create: async function (req, res) {


try {

} catch (error) {

}
db.Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
.then(()=> {
res.redirect('/movies');
})
},

Y todo esto hasta aquí, lo ponemos en el Try y eliminamos lo del .then.


create: async function (req, res) {
try {
db.Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
} catch (error) {

}
},

Y ahora cambiamos db.Movies por Movies, en base a

create: async function (req, res) {


try {
Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
} catch (error) {

}
},

Y le agrego el res.redirect, ya que en la consigna decía que se tenía que redirigir a


movies.

create: async function (req, res) {

try {

Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
})
res.redirect('/movies');

} catch (error) {

}
},

Le agregamos el mensaje de error.

y acá faltaría el genre_id, que eso se añadiría dentro de la tabla movies, y a genre_id lo
sacamos de req.body.genre_id, porque así se llama el select, si nos fijamos en la vista
moviesAdd.ejs,
Porque acá en el valor de cada opción, está el id de cada género. Nosotros seleccionamos
por nombre de género (genre.name), que eso es lo amigable para el usuario, pero por
detrás se manda el genre.id. También le mandamos el error: res.status(500).send(error)

create: async function (req, res) {

try {

Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
genre_id: req.body.genre_id
})
res.redirect('/movies');

} catch (error) {
res.status(500).send(error)
}
},
Antes que sigan, consulta para los profes, porque yo estaba atrasado con el playground, y
ayer me puse las pilas y empecé a ver y creo que llegué a esa parte del uso del del
req.body. ¿El req.body lo que hace es capturar todo lo que tenemos de la vista actual?
¿Todos los datos?
Claro, cuando nosotros cuando nosotros hacemos clic en enviar o guardar o lo que sea, se
guarda toda esa información en el body, o sea todo lo que se envía por el body con el
input que tenga la etiqueta name, o el atributo name, va a llegar al body o sea, te va a
llegar por ejemplo: req.body.name o rec.body.title, rec.body.rating, rec.body.awards, etc.
Esos son inputs que vos tenés configurado en el formulario.
¿En el formulario en que está actual en ese momento? Digamos de la vista en la que
estemos actualmente. Ahora estamos en la vista moviesAdd, por ejemplo.
No es en la que estamos.
Vamos a moviesAdd.ejs.
Nosotros ahí planeamos crear un formulario, para recibir toda la información que
necesitamos para crear un nuevo producto. Bueno, nosotros en el action, le decimos hacia
dónde va a apuntar este formulario, cuando el usuario haga clic en enviar. En este caso, se
va a enviar por POST, por lo tanto va a ir como encriptado, y la acción va a apuntar a
/movies/create.

En la ruta, vamos a tener que hacer un enrutador que sea del tipo POST, para que reciba
toda esa información, y con el req.body vamos a tener el que dice name= “title”, el que
dice name= “rating”, el que dice name= “awards”. Y todo eso, te llega con req.body.title,
req.body.id, req.body.rating, req.body.awards y demás.
Sería todo lo que está dentro del formulario y que tiene la etiqueta name. Porque si no tiene
la etiqueta name, o el atributo name. Quería preguntar: ¿Todo lo que sea name, es lo que
trabajamos del lado del servidor?
¿Y todo lo que sería id, sería todo lo que trabajamos de la parte local, del lado del cliente?
Exactamente. Del lado del cliente, de la forma más sencilla es atraparlo por el id.
Yo no sé por qué, en mi mente, el req.body, me parecía como un select. Lo entendí mal,
digamos.
No, O sea el req.body sería el objeto que se arma, o el cuerpo que viene, cuando se envía
el formulario.
Sería como una variable global, que va almacenando todo.
No global, sinó que es específico de cuando se ejecuta ese método, de lo que recibe el
formulario. No es global.

Y acá falta el await. Y creo que eso estaría.

create: async function (req, res) {

try {
await Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
genre_id: req.body.genre_id
})
res.redirect('/movies');

} catch (error) {
res.status(500).send(error)
}
},
Y creo que eso está bien, porque no hay que mandar confirmación ni nada. ¿No hay que
guardarlo en una variable y ver si salió? Creo que no.
Decía en la consigna que tenía que redirigir a movies (ya estaba esto, pero cuando
reescribimos el código no lo habíamos hecho de nuevo a esto)

create: async function (req, res) {

try {

await Movies.create({
title: req.body.title,
awards: req.body.awards,
rating: req.body.rating,
release_date: req.body.release_date,
length: req.body.length,
genre_id: req.body.genre_id
})
res.redirect('/movies');

} catch (error) {
res.status(500).send(error)
}
},
¿Vamos a hacer validaciones?
Lo pueden hacer después. Eso me olvidé de mostrarles también, de sequelize, que se
podía hacer validaciones, pero sería algo extra.
Hacemos npm run dev.

Vamos a hacer una prueba a ver si funciona.

Hacemos clic en Agregar.


Ahí me devolvió Prueba. Vamos a ver si se guardó en la BD.

Pero le creó el created_at y el update_at. Esto es por como debe estar definido el modelo.
¿Creó un timestamps? Sí. Vamos a ver en el modelo de Movie.
Está definido así, en este proyecto, pero en general no estarían created_at y update_at y
timestamps sería falso. Pero como este es el proyecto que me dan en el PDF, bueno acá
está así.

Esto termina ahí, si no se van a hacer las validaciones. Sí, las validaciones para después.

Hacemos la edición de una película - movies/edit/:id (GET)

Hacemos la primer consigna: Agregamos el botón Modificar

Vamos a routes/moviesRoutes.js, y descomentamos la ruta correspondiente.


const express = require('express');
const router = express.Router();
const moviesController = require('../controllers/moviesController');

router.get('/movies', moviesController.list);
router.get('/movies/new', moviesController.new);
router.get('/movies/recommended', moviesController.recomended);
router.get('/movies/detail/:id', moviesController.detail);
//Rutas exigidas para la creación del CRUD
router.get('/movies/add', moviesController.add);
router.post('/movies/create', moviesController.create);
router.get('/movies/edit/:id',
moviesController.edit);
// router.put('/movies/update/:id', moviesController.update);
// router.get('/movies/delete/:id', moviesController.delete);
// router.delete('/movies/delete/:id', moviesController.destroy);

module.exports = router;

Vamos a moviesDetail.ejs. y ahora s


Viendo como estaba en moviesList.ejs,
Podemos copiar esa línea, y ponerlo acá, en moviesDetail.ejs.
<a class="botonAgregar"
href="/movies/add">Agregar
una Pelicula</a>

Cambiamos botonAgregar por botonModificar y cambiamos


"/movies/add" por "/movies/edit".
<a class="botonModificar"
href="/movies/edit">Agregar una
Pelicula</a>

Y le agregamos la variable ejs movie.id.


<a class="botonModificar"
href="/movies/edit/<%= movie.id %>">Agregar
una Pelicula</a>

Eso debería funcionar: Vamos a Avatar


En el botón dice Agregar una película, eso tenemos que cambiarle

Y le cambiamos la leyenda Agregar una Pelicula por Modificar.


<a class="botonModificar" href="/movies/edit/<%= movie.id
%>">Modificar</a>
Bien, modificar ya está. Ahora hay que hacer la función edit en
controllers/moviesController.js.

Hacemos la magia en función edit, para editar una película


edit: function (req, res) {

},

Es parecida a la función add, así que la copiamos.

41:03
Pegamos en la función edit

edit: function (req, res) {


try {
const allGenres = await Genres.findAll();

res.render('moviesAdd', { allGenres })

} catch (error) {
res.status(500).send(error);
}
},

Como es una función asincrónica, debemos agregarle el async antes de la palabra


function.

edit: function async (req, res) {


try {
const allGenres = await Genres.findAll();

res.render('moviesAdd', { allGenres })

} catch (error) {
res.status(500).send(error);
}
},

Y falta un objeto más. alGenres es uno de los objetos que se va a mandar, antes se tiene
que mandar otro objeto.
edit: function async (req, res) {
try {
const allGenres = await Genres.findAll();

res.render('moviesAdd', { algo, allGenres })

} catch (error) {
res.status(500).send(error);
}
},

Que es el que va a sacar las películas. Pero vamos a ver cómo aparece en moviesEdit.ejs.
En la imagen se puede ver cómo lo recibe a este objeto: está escrito con mayúsculas.

Volvemos a controllers/moviesController.js, y creamos la const Movie. ¿Que tiene que


devolver? La película que hemos seleccionado, o sea que la tienes que buscar por id, o sea
con findByPk y la mandamos como parámetro a moviesAdd.ejs.
edit: function async (req, res) {
try {
const allGenres = await Genres.findAll();

const Movie = await Movies.findByPk();

res.render('moviesAdd', { Movie, allGenres })

} catch (error) {
res.status(500).send(error);
}
},

¿Y de dónde saco el id? Va por parámetro, lo traigo de req.body.


Ahí es donde me confundo, porque no sé si lo tiene que tomar del req.body.
Y no, porque creo que lo está pasando por la URL.
La URL que estaban armando ustedes, con el botón, era /movies/edit/ y el numerito de la
película.
Pero profe, ¿supuestamente el id no tendría que ir por la barra de direcciones, no? ¿O no
importa?
Claro, sí. Pero ustedes le agregan ese valor dinámico, para que justamente no sea una vista
para cada una de las películas que quieren editar.
No, yo lo que pregunto es al ser un id, no es problema de que vaya por por la URL, al ser
una clave?
No, no habría problema. Es prácticamente la forma que usamos para mostrar los productos
y todo eso, con los ejemplos que hacíamos.
Probamos. Hacemos clic en Modificar.

Y nos da el siguiente error.


Eso es por la relación. El error que sale acá, es por esto:

Específicamente es por cómo se llama la relación que hemos definido entre películas y
géneros, desde el lado de películas. Es ¿Es por el alias?
Exacto, el as.

Una opción era hacer un console.log para ver que llegaba.


Y ahí que te dice, en donde te daba el error?
Genre.id dice.
¿Hicieron un console.log para ver que llega? Veamos primero antes de que lo envíen a la
vista.

edit: function async (req, res) {


try {
const allGenres = await Genres.findAll();

const Movie = await Movies.findByPk();


console.log(Movie);
res.render('moviesAdd', { Movie, allGenres })

} catch (error) {
res.status(500).send(error);
}
},

Probamos nuevamente.

Veamos qué dice la consola.


44:42

Pero miren que acá están trayendo solamente la movie sin ninguna relación. Falta el
include.

edit: function async (req, res) {


try {
const allGenres = await Genres.findAll();
const Movie = await Movies.findByPk(req.params.id, {include:
['genre']});

console.log(Movie);
res.render('moviesEdit', { Movie, allGenres })
} catch (error) {
res.status(500).send(error);
}
},

Vamos a probar.

Lo único que no se graba acá es la fecha.

No llega bien por el formato. Pero funciona. Lo trae a ciencia ficción, etc. Entonces esto
sería mostrar la vista que tiene el formulario de edición de peliculas. ¿Y ahora que seguía?
Hasta ahí vamos, después va un método por post o por put, ¿No sé si seguimos profe, o ya
no?
Ya estamos justos con el tiempo, así que va a quedar pendiente y justo me estoy
acordando, hablando de eso de edición, que no vimos cómo se manipulaba la fecha, porque
quedó vacío el campo. Eso lo vamos a ver la próxima clase, vamos a hacer un proyecto, ya
voy a pensar de qué, como de ejemplo, y justamente vamos a manipular la fecha.
Directamente mostramos eso la próxima clase, cuando empecemos y después arrancamos
con el proyecto.
En el video en playground, tampoco lo muestra, profe.
¿A la fecha? Ya vamos a ver cómo lo solucionamos. Seguramente tengamos que formatear
o algo por el estilo. Pero le vamos a encontrar la vuelta. Estamos desocupados. Nos vemos
la próxima clase. Justamente en la próxima clase, ya nos queda la clase práctica y como
mencionaba Fran, la próxima clase vamos a hacer la presentación del Sprint, así que ahí ya
seguro conversamos un poquito. En esa clase vamos a tener más tiempo. Nos estamos
viendo el jueves 11.

También podría gustarte