Guide for Reviewing Rails Blog Associations & Validations - learn-co-curriculum/rails-blog-associations-validations GitHub Wiki
Schema
1) Initial migrations
The only model that is set up initially is Post
. We'll need users and tags, and we'll have to add a content
column to the posts
table.
Use rails g model --no-test-framework
for generating the models, and rails g migration add_content_to_posts
to add the column we need.
Review what is created with different rails generators, from the most stuff down: scaffold (almost never use), resource, model, controller, migration. Why do we not need --no-test-framework
when we generate a migration?
2) Creating Associations
user_id
to posts
Adding The spec is looking for a user_id
column in the posts
table. What does this tell us about the relationship between users and posts?
We'll add that column along with a belongs_to :user
in the Post
model. How do we reciprocate this?
We need a has_many
in our User
model to make the connection complete.
Creating a Join Table
How are tags related to posts?
A post can have many tags, and a tag also can have many posts. How do we connect these two models to create a many to many relationship?
By convention, we can name our new table post_tags
. We'll also need a model in addition to a migration, so which rails generator should we use?
rails g model post_tag --no-test-framework
Which two columns does our join table need? We'll need a foreign key for posts
and a foreign key for tags
.
Connecting posts and tags using ActiveRecord macros
How do we connect these two models through the join table?
In the PostTag
model:
belongs_to :post
belongs_to :tag
In the Post
model:
has_many :post_tags
has_many :tags, through: :post_tags
In the Tag
model:
has_many :post_tags
has_many :posts, through: :post_tags
Validations
1) Validating for Presence of a Field
When dealing with user input, most of the time we'll want to have some way of checking their input and making sure it is valid before persisting things to the database.
For example, we would not want to allow users to create a post with no name or no content.
How do we create a validation for the presence of name
in the Post
model?
validates :name, presence: true
We can list as many attributes as we want in a single validation. So validates :name, :content, presence: true
will make two more tests pass.
note: validates_presence_of :name, :content
would also work!
We also need to make sure a new user cannot be created without a name.
2) Validating for Uniqueness of a Field
We don't want duplicate tags to be created, so we want to make sure the name
of a tag is unique before persisting it.
How do we validate for uniqueness of name
in the Tag
model?
validates :name, uniqueness: true
or
validates_uniqueness_of :name
We'll need the same validation in our User
model.
If we had validates :name, presence: true
in the User
model, we can just chain the uniqueness
validation on to that:
validates :name, presence: true, uniqueness: true
Creating New Posts
Routing
First off, there are no routes defined! Now would be a great time to create some in config/routes.rb
. We can use resources :posts
to get ALL the restful routes for the posts resource.
How else can we define the routes we need for creating posts? Which specific routes are needed and how could we create them one at a time?
This is a good point to talk about arguments to the resources
macro (only
, except
) and also how to define routes without resources
.
Controller
The PostsController
seems to have already been generated for us - in fact, the boilerplate code we see there is the exact code we get when we use rails g resource
. So much stuff, most of it we might not even need or understand for that matter.
At this point if there is time you may want to scrap the generated create
and update
actions in the PostsController
and build them out from scratch as you build out the forms.
Which actions do we know we need for creating posts? We need to 1) render the form and 2) create the new post. We'll also probably want a third route to redirect to after the post is created!
Form for Creating Posts
If we look in app/views/posts
there are a lot of generated files in here already. Discuss each file that got generated by rails g scaffold
.
What is app/views/posts/_form.html.erb
? Why does this file name begin with an underscore? Discuss how partials work, check for student understanding.
Creating Posts with Content
We need a field for content
, but that is not enough to get the next test passing. We get an error saying that the post could not be saved because the name cannot be blank!
Discussion: Why did this error happen? Our form has the correct field. If we put a pry at the top of the create
action we see that content
made it into params... but not into post_params
! Explanation of strong params should go here.
Creating Posts with Tags
We'll need to use the collection_check_boxes
helper for this. Look at the documentation.
If you haven't done so yet, now is a good time to migrate the development database, run rake db:seed
, and start up the server so we can look at the form while we work on it.
Adding <%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %>
gets what we need into the form.
Based on what happened with the content
field when we tried to create a new post, what do we need to do in order to let the tag_ids
through in our post_params
?
Note: Since tag_ids
is going to come through as an array, we need to specify this structure in post_params
. Use a binding in the create
action to look at what the tag_ids
look like in params.
Adding :tag_ids => []
to the permitted fields in post_params
gets everything working but the test isn't passing. Why is this test failing? How do we know if the failure has to do with our schema, our routes, our controller, our form, or something else?
As it turns out, the last test fails simply because our post show page is not displaying the tags. Fix this and we're done!