Skip to content

Instantly share code, notes, and snippets.

@OfTheDelmer
Created December 11, 2014 23:50
Show Gist options
  • Save OfTheDelmer/1b92691dc30f81600027 to your computer and use it in GitHub Desktop.
Save OfTheDelmer/1b92691dc30f81600027 to your computer and use it in GitHub Desktop.

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

HW Review

Model Associations

Getting Started

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

Questions

  • 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);  
    },
...

Questions

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.

Migrating

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.

Questions


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... 

More Migrations

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();
  }
};

Questions


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.

Playing With Your Models

Let's play with our models

Exercises (if you want to try):

  1. Create an author with name john doe and age of 32.
  2. Create a post with the title All About John and content blah blah blah john this and john that.
  3. Harder: Update the above post to be associated to john. Hint: you'll need to use john's author id.
  4. Easier Than above: Create a new post associated to the author we created.

Solutions

  1. 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.

    Questions

    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...
    
  2. 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);
    ... });
    
  3. 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);
    ...})
    
  4. 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);
    ...     });
    ... });
    

    Questions?

    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

15 min break, my legs are falling asleep.

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.

After Break

Setting Up App.js

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




*/




Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment