Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Requisitos previos
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.
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.
return Movie
};
Vamos al modelo Genre.js.
Y el siguiente requisito es que un Genre tiene muchas películas.
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");
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.
return Movie
};
Vamos al modelo Actor.js
Por Actor.
Por Actor.
a actor_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.
let config = {
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: false
}
const Actor = sequelize.define(alias, cols, config);
return Actor
};
Micro desafío - Paso 2
Vamos a routes/moviesRoutes.js.
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.
¿Qué tiene que hacer esto? Mostrar el formulario de creación de la película, movieAdd.
De la carpeta View, moviesAdd.ejs.
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;
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>
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.
Y lo pegamos en moviesDetail.ejs.
Actualizamos moviesDetail.ejs.
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
En la función add,
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.
} catch (error) {
}
res.render('moviesAdd', { algo });
},
}
},
¿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),
}
},
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.
}
},
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)
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:
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;
En este caso, no estamos utilizando async, así que le agregamos el async antes de la
función para transformarla en asincrónica.
} 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');
})
},
}
},
}
},
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) {
}
},
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)
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.
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)
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.
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.
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;
},
41:03
Pegamos en la función edit
res.render('moviesAdd', { allGenres })
} catch (error) {
res.status(500).send(error);
}
},
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();
} 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.
} catch (error) {
res.status(500).send(error);
}
},
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.
} catch (error) {
res.status(500).send(error);
}
},
Probamos nuevamente.
Pero miren que acá están trayendo solamente la movie sin ninguna relación. Falta el
include.
console.log(Movie);
res.render('moviesEdit', { Movie, allGenres })
} catch (error) {
res.status(500).send(error);
}
},
Vamos a probar.
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.