La palabra CRUD significa Create(Crear), Read(Leer), Update(Modificar), Delete(Eliminar); y se refiere a las operaciones que podemos realizar en los registros de una base de datos, en el caso de MongoDB, en los documentos. En la publicación pasada, aprendimos cómo conectar Node.js a una base de datos MongoDB, en esta ocasión vamos a ver como insertar, modificar, eliminar y recuperar documentos.
Recordar siempre tener el servicio de MongoDB encendido.
Yo en mi caso usaré el proyecto de la publicación pasada, «conexion_mongodb», pero si querés crear un nuevo proyecto, adelante.
Ahora vamos a ir a nuestro archivo app.js y vamos a importar el módulo de mongoose, si es que no está:
var mongoose = require('mongoose');
Y vamos a conectarlo a MongoDB, como hicimos la última vez:
mongoose.connect('mongodb://localhost/primer_base', function(error){ if(error){ throw error; }else{ console.log('Conectado a MongoDB'); } });
Creando nuestro Modelo
Al igual que las tablas en una base de datos SQL, en Mongoose también se trabaja con el concepto de Modelo. Si no sabés qué es un modelo, es una clase, que se encargará de realizar consultas, en un tipo de documento específico. En nuestro caso vamos a crear un modelo para guardar personajes. Así que vamos a agregar las siguientes líneas:
var PersonajeSchema = mongoose.Schema({ nombre: {type: String, required: true}, apellido: {type: String, required: true}, biografia: {type: String, required: true} }); var PersonajeModel = mongoose.model('Personaje', PersonajeSchema);
En primer lugar creamos el esquema de nuestro modelo, mediante el método de mongoose, Schema(). Aquí vamos a definir los atributos que tendrá el esquema. Uno para guardar el nombre, otro para el apellido y un último para la biografía. Los tres serán de tipo String y obligatorios:
var PersonajeSchema = mongoose.Schema({ nombre: {type: String, required: true}, apellido: {type: String, required: true}, biografia: {type: String, required: true} });
Finalmente con el método model(), crearemos un modelo con el nombre ‘Personaje’, y le pasamos el esquema, la estructura del modelo:
var PersonajeModel = mongoose.model('Personaje', PersonajeSchema);
CRUD
Para disponer de las acciones para nuestros documentos de tipo Personaje, vamos a crear primero dentro de la carpeta routes, un nuevo módulo al que llamaremos personajes.js. Y las vistas, así que vamos también a la carpeta views, y dentro creamos un directorio con el nombre: personajes, y dentro tres vistas: index.jade, show.jade y save.jade.
Vamos a editar nuestro módulo personajes.js, de la siguiente forma:
var Personaje; exports.setModel = function(modelo){ // }; exports.index = function(req, res){ // }; exports.create = function(req, res){ // }; exports.store = function(req, res){ // }; exports.show = function(req, res){ // }; exports.edit = function(req, res){ // }; exports.update = function(req, res){ // }; exports.destroy = function(req, res){ // };
En primer lugar tendremos una variable Personaje que representará al modelo dentro de ese módulo:
var Personaje;
Luego tendremos un método para setear esa variable:
exports.setModel = function(modelo){ // };
Así que vamos a reemplazar ese método de esta manera:
exports.setModel = function(modelo){ Personaje = modelo; };
Con este método definiremos el modelo, que estará disponible dentro del resto del módulo, como dijimos antes. Para ello vamos a llamar a este método y vamos a pasarle dicho modelo.
Así que vamos a modificar el archivo app.js y reemplazamos lo anterior por el siguiente código:
var mongoose = require('mongoose'); var personajes = require('./routes/personajes'); mongoose.connect('mongodb://localhost/primer_base', function(error){ if(error){ throw error; }else{ console.log('Conectado a MongoDB'); } }); var PersonajeSchema = mongoose.Schema({ nombre: {type: String, required: true}, apellido: {type: String, required: true}, biografia: {type: String, required: true} }); var PersonajeModel = mongoose.model('Personaje', PersonajeSchema); personajes.setModel(PersonajeModel);
Y luego agregaremos siete acciones con los otros métodos del módulo personajes:
app.get('/personajes', personajes.index); app.get('/personajes/create', personajes.create); app.post('/personajes', personajes.store); app.get('/personajes/:id', personajes.show); app.get('/personajes/:id/edit', personajes.edit); app.put('/personajes/:id', personajes.update); app.delete('/personajes/:id', personajes.destroy);
Verbos HTTP
Como se ve en las anteriores líneas, hemos utilizado dos métodos nuevos: put() y delete(). Las acciones get() son para mostrar registros, post() para insertar nuevos registros, put() para modificar y delete() para eliminar.
En mi caso yo creé dentro del módulo personajes, métodos para las distintas acciones: index() (GET) nos mostrará una lista con los personajes creados. create() (GET) cargará un formulario para insertar un nuevo personaje, y store() (POST) será justamente la acción que reciba esos datos y hará creará un nuevo documento. show() (GET) nos mostrará un personaje específico. edit() (GET) será un formulario para editar los datos de un personaje en concreto, mientras que update() (PUT) será la acción que reciba esos nuevos valores para modificar el personaje. Y finalmente destroy() (DELETE) para eliminar un personaje.
Listar registros
Vamos a empezar con la acción index() de nuestro módulo personajes.js. Aquí vamos a mostrar la lista con todos los personajes registrados. Así que modificamos esa función con el siguiente código:
exports.index = function(req, res){ Personaje.find({}, function(error, personajes){ if(error){ res.send('Ha surgido un error.'); }else{ res.render('personajes/index', { personajes: personajes }); } }) };
Como se ve en el ejemplo, llamamos al método find() del modelo Personaje.
Este recibirá como primer parámetro las condiciones de la búsqueda, como aquí queremos recuperar todos los personajes, sin filtros, entonces ingresamos un json vacío. También podríamos por ejemplo buscar todos los personajes que cumplan una característica:
{apellido: 'Simpson'}
Con esto nos traerá sólo aquellos personajes de apellido ‘Simpson’.
El segundo parámetro es un callback, que recibirá dos parámetros. El error, si ha habido alguno al intentar realizar la consula, y un array con los documentos encontrados. Entonces vamos a preguntar si hay algún error, y de no haberlo cargaremos la vista index.jade, a la cual le pasamos ese array:
if(error){ res.send('Ha surgido un error.'); }else{ res.render('personajes/index', { personajes: personajes }); }
Ahora, podemos continuar editando justamente esa vista. Así que editamos index.jade con el siguiente código:
extends ../layout block content h1 Personajes p a(href="/personajes/create") Crear nuevo personaje -if(personajes.length > 0) table thead tr th Nombre th Apellido th th th tbody -each item in personajes tr td #{item.nombre} td #{item.apellido} td Ver td Editar td Eliminar -else p No existen personajes creados
En esta vista preguntamos si existen registros, mediante la propiedad length del array personajes:
-if(personajes.length > 0)
De haber registros encontrados dibujamos una tabla con los personajes:
Por el momento las enlaces a «ver», «modificar» y «eliminar» registros no hará nada, pero de eso nos encargaremos más adelante. Sin embargo sí habrá un enlace que nos va a redireccionar a la acción create(). Vamos ahí.
Agregar registros
Las acciones de create() y store() que he definido, servirán para mostrar el formulario para agregar un nuevo registro, y la segunda será el que reciba esos datos del formulario e inserte el registro. El primero será una acción de tipo GET y el segundo POST.
La acción create() tendrá el siguiente código:
exports.create = function(req, res){ res.render('personajes/save', { put: false, action: '/personajes/', personaje: new Personaje({ nombre: '', apellido: '', biografia: '' }) }); };
En esta acción vamos a cargar la vista save.jade. Vale aclarar que esa vista será usada también como formulario para modificar el registro, por la acción edit(), por tanto debemos pasar una serie de parámetros para diferenciar a ambos.
En primer lugar yo le indique una propiedad put, igualada a false, ya que este formulario será enviado por POST, a diferencia del de edición que enviará el formulario por PUT. Ya veremos esto. También le indique la acción, a dónde debe ir ese formulario. Y un objeto personaje, con sus propiedad nombre, apellido y biografia, como valores vacíos. Esto será porque ese formulario recibirá en sus campos valores, y como se está intentando insertar un registro nuevo, entonces los campos tendrá que estar vacíos.
Ahora vamos a editar el archivo save.jade con el siguiente código:
extends ../layout block content h1 Guardar personaje form(method="post", action="#{action}") -if(put) input(type="hidden", name="_method", value="PUT") label Nombre br input(type="text", name="nombre", required="required", value="#{personaje.nombre}") br label Apellido br input(type="text", name="apellido", required="required", value="#{personaje.apellido}") br label Biografía br textarea(name="biografia", cols="50", rows="5", required="required") #{personaje.biografia} br input(type="submit", value="Guardar") a(href="/personajes") Cancelar
Aquí tenemos un formulario normal, con campos para ingresar, el nombre, apellido y biografía del personaje.
Con respecto a la línea:
-if(put) input(type="hidden", name="_method", value="PUT")
Por el momento vamos a omitirla, ya la veremos cuando tengamos que editar.
Ahora vamos a seguir yendo a nuestra acción store() que es donde recibirá los datos y guardará el documento:
exports.store = function(req, res){ var personaje = new Personaje({ nombre: req.body.nombre, apellido: req.body.apellido, biografia: req.body.biografia }); personaje.save(function(error, documento){ if(error){ res.send('Error al intentar guardar el personaje.'); }else{ res.redirect('/personajes'); } }); };
Aquí simplemente creamos un json Personaje, con los valores enviados por el usuario:
var personaje = new Personaje({ nombre: req.body.nombre, apellido: req.body.apellido, biografia: req.body.biografia });
Y finalmente lo guardamos con el método save(). El cual recibirá como parámetro un callback, que a su vez tendrá dos parámetros. Uno con el error, si es que hubo, y otro con el documento que se acaba de insertar. Finalmente si todo salió bien redireccionamos a la página de la lista de personajes.
Mostrar un registro
Vamos a entrar nuevamente a nuestra vista index.jade, y vamos a modificar la siguiente línea:
td Ver
Por:
td a(href="/personajes/#{item._id}") Ver
La cual nos permitirá acceder al id de ese registro.
Así que ahora editaremos la acción show(), con el siguiente código:
exports.show = function(req, res){ Personaje.findById(req.params.id, function(error, documento){ if(error){ res.send('Error al intentar ver el personaje.'); }else{ res.render('personajes/show', { personaje: documento }); } }); };
Mediante el método findById(), podremos recuperar un documento por su id. El primer parámetro será justamente el id. Mientras que el segundo será un callback, en donde el primer parámetro será un error, si hubo alguno al intentar recuperar el registro. El segundo parámetro es un objeto con los datos del registro.
Si todo ha salido bien cargaremos la vista show.jade, y le pasaremos el objeto con los datos del personaje.
Así que ahora podemos modificar la vista show.jade con el siguiente código:
extends ../layout block content h1 #{personaje.nombre} #{personaje.apellido} div(style='white-space:pre;') #{personaje.biografia} br a(href="/personajes") Volver atrás
Modificar registros
Vamos a abrir nuevamente la vista index.jade y vamos a reemplazar la línea:
td Editar
Por:
td a(href="/personajes/#{item._id}/edit") Editar
De este modo le agregamos un enlace al formulario para editar el registro.
Ahora vamos a crear nuestra acción edit() con el siguiente código:
exports.edit = function(req, res){ Personaje.findById(req.params.id, function(error, documento){ if(error){ res.send('Error al intentar ver el personaje.'); }else{ res.render('personajes/save', { put: true, action: '/personajes/' + req.params.id, personaje: documento }); } }); };
Primero recuperamos el registro con el método findById(), una vez recuperado, lo pasamos a la vista save.jade, la misma que usamos en el método create(). Sin embargo acá, además además de agregar una url diferente en la acción a enviar el formulario (con el id), en este caso la propiedad que le pasamos, put, será igual a true. Esto, es porque en este caso, este formulario enviará una petición de tipo PUT al servidor.
Si nos fijamos bien, dentro de la vista save.jade, tenemos una línea:
-if(put) input(type="hidden", name="_method", value="PUT")
exports.update = function(req, res){ Personaje.findById(req.params.id, function(error, documento){ if(error){ res.send('Error al intentar modificar el personaje.'); }else{ var personaje = documento; personaje.nombre = req.body.nombre; personaje.apellido = req.body.apellido; personaje.biografia = req.body.biografia; personaje.save(function(error, documento){ if(error){ res.send('Error al intentar guardar el personaje.'); }else{ res.redirect('/personajes'); } }); } }); };
Eliminar registros
Vamos a modificar por última vez el archivo index.jade, donde la línea:
td Eliminar
Por:
td form(method="post", action="/personajes/#{item._id}") input(type="hidden", name="_method", value="DELETE") a(href="javascript:void(0);", onclick="if(confirm('¿Está seguro que desea eliminar este registro?')){this.parentNode.submit();}") Eliminar
Como dijimos antes, los formularios son capaces de enviar sólo peticiones GET y POST, por tanto debemos disfrazar esta petición agregando un campo oculto con el nombre «_method» y en este caso el valor «DELETE». También tendremos un enlace con funcionalidad Javascript, que al ser pulsado preguntará si está seguro que desea eliminar el registro, y si el usuario pulsa «Aceptar» en el confirm(), enviará la petición DELETE para eliminar el documento.
Así que vamos a editar la acción que nos falta, destroy():
exports.destroy = function(req, res){ Personaje.remove({_id: req.params.id}, function(error){ if(error){ res.send('Error al intentar eliminar el personaje.'); }else{ res.redirect('/personajes'); } }); };
Aquí simplemente eliminamos mediante el método remove(), el cual recibirá como primer parámetro el id del documento a eliminar, y como segundo un callback, que tendrá como parámetro un error, si lo hubo, como siempre.
Anterior: Introducción a Node.js, parte 12: Conexión con MongoDB
Siguiente: Introducción a Node.js, parte 14: Conectar Node.js con MySQL
Al inicio en que archivo ceo el modelo, no lo indicas
:Creando nuestro Modelo
Fijate en el repositorio de github:
https://github.com/fernandoggaitan/mongoose_crud
Y como haria un CRUD con Node.js y MySQL??
Mirá, yo acá subí como conectar la aplicación a MySQL:
http://fernando-gaitan.com.ar/introduccion-a-node-js-parte-14-conectar-node-js-con-mysql/
Lo único que necesitas saber son dos cosas, qué es CRUD y hacer consultas en MySQL, con eso te alcanza para hacer tu propia aplicación CRUD en MySQL.
Saludos!
Muy buenas Ferchu, a pesar de haber seguido el tuto al pie de la letra, me da un error al eliminar o actualizar los datos, pues el archivo app.js no entiende el app.put y el app.delete.
Si cambio estos valores por post me elimina o actualiza bien, con lo cual deduzco que se debe a que lo está enviando por post a pesar de haber puesto al formulario el name=»_method» y el value=»DELETE». Sabes que puedo hacer?
Hacé una cosa, para comprobar que no es el campo de formulario, imprimí por pantalla el valor de _method con:
req.body._method
Tal vez ese dato no esté llegando bien, y por eso entrá por POST y no por PUT o DELETE.
Saludos!