Hey everyone,
This is a live coding document created by Stypi that will allow me to share code with you as we discuss materials. You should be seeing these changes as I type.
I'll try to use a markdown style for our discussion so that we can just put this session into the notes repo for reference later. Cool?
Please use the sidebar for chatting
A good approach to the homework is to setup your models before you get to far into trying to write application code.
To setup our models we first need to do a sequelize init
. This creates the models/, migrations/, and config/
folders. Inside the config.json
we need to setup our database config for development, the default used by sequelize.
development: {
database: 'model_hw', // the name of the db we need to create
host: '127.0.0.1', // or localhost
dialect: 'postgres' // not mysql
}
So, now that we have defined our database in the config.json
we can continue on with setting up our models... our at least we think we should? Let's see what happens.
sequelize model:create --name author --attributes 'firstName:string, lastName:string, age:integer'
sequelize model:create --name post --attributes 'title:string, content:text'
Note: in the above creation of the post
model we have left off something rather important, which is the foreign key
to the author or authorId:integer
, because of this we will have to deal with the problems later.
Let's go ahead and setup our model associations
models/author.js
...
associate: function (models) {
/*
This tells our model that
it has an associated set
of posts and so sequelize
will create helper methods
for us to access those, i.e.
`getPosts()`
*/
this.hasMany(models.post);
}
...
HeHe
- Do we all understand what the
this.hasMany(models.post)
is doing for us?
Please Ask A Question here. In this little box. Any Questions?
Right, Sam, it is just setting up the association and giving us helper methods on the
author model for the post
Jeff: What is a helper method?
Answer: It is a method that you didn't quite define anywhere but was created for to help with doing something that you'd routinely do. You can also explicitly create a helper method to help you solve routine tasks that you do, but in this case I am referring to helper methods that sequelize just magically creates after you say `this.hasMany(...)`
Let's continue on with the review
models/post.js
...
associate: function (models) {
/*
The belongsTo statement
sets up the reverse association
so that we can look up
authors associated with
a particular post, i.e.
getAuthor()
*/
this.belongsTo(models.author);
},
...
Please write questions here.
Mark: Does `this.belongsTo(models.author)` go with the helper method above?
or inside a differnt scope?
ANSWER: The `this.belongsTo(models.author)` is a method that creates helper methods on our `post` model for the `author`. It allows us to say things like `somePost.getAuthor()`, which will do a SQL lookup for the `author`?Does that help?
Mark: How does this block look in reference to (models.post)>?
Nested?
ANSWER: I'll just say that it creates instance methods on the `post`, but it doesn't do anything to the `models.author`.
Mike:
models/post.js
...
associate: function (models) {
this.hasMany(models.post);
},
...
Del: So are you wondering what this would do?
I am going to continue.
Earlier we setup our config.json
with the info that described our database, but if we try to run
sequelize db:migrate
We get an error. This is because we never created our database, and so, we should do that.
createdb model_hw
It's okay if you forget to do this because sequelize will let you know that you forgot to do it when you go to try to migrate.
Sam: In the creation of the model and the database, do we have to specify id and/or author_id?
ANSWER: I mentioned that earlier we didn't specify the foreign key and that we need one to reference the associated author. The solution that we learned in the notes was to do a `db.sequelize.sync()` before starting our server, which tells sequelize to analyze our models for neccessary changes and then make those in the db. However, this has drawbacks.
If you change associations or mess up your model it effects your db tables, and we want to record any change that ever happens in our database with a migration. The solution to this problem is just to create a migration that adds the foreign key to our database. Let's continue to this...
Earlier we created our author
and post
model, but forgot to add the authorId
to our posts
table in our database. Without the authorId
we wouldn't be to setup the proper association, and we need to get used to writing migrations. Let's write one to add this column.
sequelize migration:create --name add_author_id_to_posts
This just creates a blank timestamped file
migrations/20141210..._add_author_id_to_posts.js
module.exports = {
// This is what will happen when we run `db:migrate`
up: function(migration, DataTypes, done) {
// This is our migration to create an authorId
migration.addColumn("post", "authorId", {
type: DataTypes.Integer
});
done();
},
// This is what will happen when we run `db:migrate:undo`
down: function(migration, DataTypes, done) {
// This is our migratino to remove the column
migration.removeColumn("post", "authorId");
done();
}
};
Alexandra: If last night we tried to do this, but it didn't work. Will it hurt to create a new migrations file when we already created one for this?
ANSWER: No.
Alexandra: or should we just edit what it is in that new migrations file to match what you put here?
ANSWER: You can also edit the old migration file if you want and then just migrate it once you're done.
Sam: I got the following error "Task 'migrations:create' was not defined in your sequelizefile but you tried to run it."
Allison: I got that same thing
ANSWER: note the added `s` to `migration`. (Sam:thumbs up)
Mike: you did that del ;) I fixed it above already though
Allison: mine didnt work so I tried copy and paste of Dels but same error- will try again
Del: I made an oopsy...
Mike: If you got another error, you might not have ran `sequelize init`
Allison: it worked with the fix- thanks Mike!
Del: Feel free to always run `sequelize help` to check the methods available to you
Here we go again, now we want to see if our tables are setup correctly, and you can do this by connecting to your db.
psql model_hw
then check out the details of your table
\d+ authors
\d+ posts
Note that your tables are automagically pluralized.
Let's play with our models
- Create an
author
with namejohn doe
and age of32
. - Create a
post
with the titleAll About John
and contentblah blah blah john this and john that
. - Harder: Update the above post to be associated to
john
. Hint: you'll need to use john's author id. - Easier Than above: Create a new
post
associated to the author we created.
-
We need to create the author to verify it's working. Let's go into the node REPL (type node). The require your models.
> var db = require("./models"); > // we want to create an author named "john Doe" of age 32 > db.author.create({firstName: 'john', lastName: 'doe', age: 32}).then(function (newAuthor) { ... console.log(newAuthor.dataValues); ... });
The above should print the dataValues of the newly created author. If you get an error then you might need to fiddle with your migrations and database.
Sam: Could we have inserted our new author into the database through psql? Magic Mike (aka Delmer (sure... (map #(delmer is $1) [mean, really mean, not funny] (nerd rage over 9000)): yeah, but dont The Real Mike: yeah, but dont Del: indeed Mike: Sorry Lisp geeking out...
-
Moving on... We want to create a post
> db.post.create({title: "All About John", > ... content: "blah blah blah john this and john that"}).then(function (newPost) { ... console.log(newPost.dataValues); ... });
-
Let's try the harder exercise. Assuming that our author has id 1, but if you look at the console.log from exercise 1 then you can use the actual author id. Also, let's assume the post we want to update has
id
1.> db.post.find(1).then(function (foundPost) { ... foundPost.updateAttributes({ ... authorId: 1 ... }).then(function(updatedPost) { ... console.log(updatedPost.dataValues); ...})
-
Let's try to create a
post
that is associated to our author we created in exercise 1. I'll john had id 1, but if you check the dataValues you logged to the terminal you should see the id of john on your database.> db.post.create({title: 'Another Post About John', ... content: 'stuff about john', ... authorId: 1}).then(function (newPost) { ... console.log(newPost.dataValues); ... newPost.getAuthor().then(function (author) { ... console.log(author.dataValues); ... }); ... });
Del: Is everyone okay with the above? Anyone? Going once? Going twice? Going thrice? AJ: what would be the steps.. from a perspective of : we sequlize:migrate, then confirm that our tables are talking to each other? gotcha. Del: These exercises are some steps to verify that our tables are talking to eachother. Alison: my table is stuck as if node is waiting for more Del: hit enter. Allison: tried that, and nope Del: ctrl+c one time Allison: thanks
Thanks
AJ: what are we doing after this? Del: I was hoping to walk through the express app.js.
Stephen: BTW...I'm hanging in Sextant Cafe on Folsom & 10th. Yeah there's a lot next door I thought you guys lived near here
Del: I am pretty sure I've been their. In fact, I used to live right there. Mike and I went there every time we got a zip car. It's the new one that was on the corner. Remember that one mike? Mike: Yeah that place was cool.
app.js
var express = require('express'),
bodyParser = require('body-parser')
db = require('./models'),
app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({extended: true});
app.get("/", function (req, res) {
res.render("site/home");
});
app.get("/authors/new", function (req, res) {
res.render("authors/new");
});
// Handle a form submit for a author, i.e. the following
/*
bootstrap: http://getbootstrap.com/css/#forms-example
<div class="container">
<div class="row">
<div class="col-sm-offset-4 col-sm-4">
<form method="post" action="/authors" >
<div class="form-group">
<input class="form-control" type="text" name="author[firstName]" placeholder="First Name">
</div>
<div class="form-group">
<input class="form-control" type="text" name="author[lastName]" placeholder="Last Name">
</div>
<div class="form-group">
<input class="form-control" type="text" name="author[age]" placeholder="age">
</div>
<div class="form-group">
<button class="btn btn-primary">Save Author</button>
</div>
</form>
</div>
</div>
</div>
*/
app.post("/authors", function (req, res) {
db.author.create({
firstName: req.body.author.firstName,
lastName: req.body.author.lastName,
age: req.body.age
}).then(function (newAuthor) {
res.redirect("/authors/" + newAuthor.id);
});
});
/*
QUESTIONS?
*/
//Show the author
app.get("/authors/:id", function (req, res) {
// we need to find the author by the id.
var id = req.params.id;
db.author.find({
where: {id: id},
include: [db.post]
})
.then(function (foundAuthor) {
res.render("authors/show", {author: foundAuthor});
});
});
// We could have also done the following:
/*
app.get("/authors/:id", function (req, res) {
// we need to find the author by the id.
var id = req.params.id;
db.author.find(id)
.then(function (foundAuthor) {
foundAuthor.getPosts()
.then(function (foundPosts) {
res.render("authors/show", {author: foundAuthor, posts: foundPosts});
});
});
});
*/
/*
We need to render a form that looks like the following
<form method="post" action="/authors/<%= authorId %>/posts/new">
<div>
<input type="text" name="post[title]">
</div>
<div>
<textarea name="post[content]"></textarea>
</div>
<div>
<button>Save Post</button>
</div>
</form>
*/
app.get("/authors/:author_id/posts/new", function (req, res) {
var authorId = req.params.author_id;
res.render("posts/new", {authorId: authorId});
});
app.post("/authors/:author_id/posts", function (req, res) {
var authorId = req.params.author_id;
db.post.create({
title: req.body.post.title,
content: req.body.post.content,
authorId: authorId
}).then(function (post) {
res.redirect("/authors/" + author_id + "/posts/" + post.id);
})
});
// there are a few ways we could grab a post associated with an author
app.get("/authors/:author_id/posts/:id", function (req, res) {
var authorId = req.params.author_id;
var id = req.params.id;
// first find the post
db.post.find(id)
.then(function (foundPost) {
// verify that it belongs to the correct author
if (foundPost.authorId === parseInt(authorId)) {
res.render("posts/show", {post: foundPost});
} else {
res.redirect("/authors/"+authorId);
}
});
});
/*
Questions:
How do we feel about the flow of the above?
Sam: Fist to five = 2.5
*/
/*
Questions?
Del: Are we okay with the two ways of getting an author and their associated posts?
### Lunch Time??? Let's be back on here
I know everyone is off to lunch, quick question, last night i did something like this:
app.get('/authors/:id', function (req,res) { var authorId = req.params.id; db.author.find(authorId).success(function(foundAuthor) { res.render('authors/show.ejs', {authorToShow: foundAuthor}); }); });
by adding the get.Post() it allows to add the post in addition to the name? correct?
sandra... when time permits..... after lunch
*/