GithubHelp home page GithubHelp logo

learn-co-curriculum / displaying-associations-rails Goto Github PK

View Code? Open in Web Editor NEW
0.0 19.0 727.0 421 KB

License: Other

Ruby 79.83% JavaScript 1.56% CoffeeScript 0.50% CSS 1.62% HTML 16.07% SCSS 0.43%

displaying-associations-rails's Introduction

Displaying Associations in Rails

Objectives

After this lesson, you should be able to...

  1. Create a has_many and belongs_to association.
  2. Build associated data through the console and db/seeds.rb.
  3. Query for associated data using methods provided by association.
  4. Embed association data within views.
  5. Iterate over associated data within a view displaying individual instances.

Blog Categories

In this lesson, we'll be setting up a blog admin panel so that Post objects can be created, associated with Category objects, and listed by Category.

The Models

First, we'll set up associated models, just like in the preceding lesson:

# app/models/post.rb

class Post < ActiveRecord::Base
  belongs_to :category
end
# app/models/category.rb

class Category < ActiveRecord::Base
  has_many :posts
end

Seed Data

Once you start working with more and more complicated data sets, you will realize that there is a lot of stuff you have to set up just to be able to play with your methods. The associations are so vast that you need to make many posts with many categories and all of that! How frustrating. What you are doing is called "seeding" the database. Pretty much putting in some test data so that you can play with your app. In Rails we set up our seed data in db/seeds.rb. Then we'll be able to just seed (or re-seed) the database with a quick rake db:seed.

# db/seeds.rb

clickbait = Category.create!(name: "Motivation")
clickbait.posts.create!(title: "10 Ways You Are Already Awesome")
clickbait.posts.create!(title: "This Yoga Stretch Cures Procrastination, Maybe")
clickbait.posts.create!(title: "The Power of Positive Thinking and 100 Gallons of Coffee")

movies = Category.create!(name: "Movies")
movies.posts.create!(title: "Top 20 Summer Blockbusters Featuring a Cute Dog")

Woot! The best thing about the seeds.rb file is that it's just Ruby! There is no magic. Look, super standard Ruby. To run the seed file in the development environment, you can activate the rake task:

rake db:seed

If you want to play around with the data, of course, it's always possible to take the create statements exactly as written above and type them into rails console.

The Views

Posts

When viewing a single post, we'll want to have a link to its category available.

<!-- app/views/posts/show.html.erb -->

<h1><%= @post.title %></h1>

<h3>Category: <%= link_to @post.category.name, category_path(@post.category) if @post.category %></h3>

<p><%= @post.description %></p>

@post.category is the Category model itself, so we can use it anywhere we would use @category in a view for that object. Also note that we added the if @post.category conditional to ensure that the view doesn't try to call @post.category.name if the post has not been associated with a category.

Categories

In this domain, the primary use of a category is as a bucket for posts, so we'll definitely have to make heavy use of associations when designing the view.

<!-- app/views/categories/show.html.erb -->

<h1><%= @category.name %></h1>

<h3><%= pluralize(@category.posts.count, 'Post') %></h3>
<ul>
  <% @category.posts.each do |p| %>
    <li><%= link_to p.title, post_path(p) %></li>
  <% end %>
</ul>

The object returned by an association method (posts in this case) is a CollectionProxy, and it responds to most of the methods you can use on an array. Think of it like an array.

If we open up rails console, we can confirm that the count results are accurate:

Post.count
 => 4
clickbait = Category.find_by(name: "Motivation")
 => #<Category id: 1, ...>
clickbait.posts.count
 => 3

Meanwhile, for listing a category's posts, we wrote a loop very similar to the loops we've been writing in index actions, which makes sense since a category is essentially an index of its posts. Let's compare them side-by-side:

<!-- app/views/categories/show.html.erb -->

...

<% @category.posts.each do |p| %>
  <li><%= link_to p.title, post_path(p) %></li>
<% end %>

...

Versus:

<!-- app/views/posts/index.html.erb -->

...

<% @posts.each do |p| %>
  <li><%= link_to p.title, post_path(p) %></li>
<% end %>

...

In fact, the only difference is what we call each on.

Recap

With Active Record's powerful association macros and instance methods, we can treat related models exactly the same as we treat directly-accessed models. As long as the database and classes are set up correctly, Active Record will figure the rest out for us!

displaying-associations-rails's People

Contributors

annjohn avatar bhollan avatar dakotalmartinez avatar danielseehausen avatar dependabot[bot] avatar gj avatar ihollander avatar jordanhudgens avatar lisamarie616 avatar lizbur10 avatar lkwlala avatar lukeghenco avatar maxwellbenton avatar mendelb avatar pletcher avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

displaying-associations-rails's Issues

Lab is broken

Spent some time talking to two different coaches. Apparently this lab is broken. Got hung up on test : "the song form creates a song with notes".

The coach mentioned I should move on considering its broken.

post_spec.rb:36 make_title_case "edit" -> "Edit"

post_spec.rb:36 reads:

    expect(page).to have_content("My edit")

but app/models/post.rb:4 modifies the title:

  before_validation :make_title_case

So, before the title is validated it will become "My Edit".

fix:

# spec/features/post_spec.rb:36

    expect(page).to have_content("My Edit")

Categories_controller_spec.rb -no category created

When this lab is cloned from github and run, this is the error I got:

Failures:

  1) CategoriesController GET #show returns http success
     Failure/Error: @category = Category.find(params[:id])

     ActiveRecord::RecordNotFound:
       Couldn't find Category with 'id'=
     # ./app/controllers/categories_controller.rb:3:in `show'
     # ./spec/controllers/categories_controller_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.07057 seconds (files took 1.44 seconds to load)
1 example, 1 failure

The spec calls erb:'show' with no id. I fixed the problem with a new instance of Category, and adding the id to the erb line, like this:

require 'rails_helper'

RSpec.describe CategoriesController, type: :controller do

  before do
    @category = Category.create!(name: "The Dangers of Stairs")
  end

  describe "GET #show" do
    it "returns http success" do
      get :show, id: @category
      expect(response).to have_http_status(:success)
    end
  end

end

I know we shouldn't edit the specs, but I couldn't find another way to pass the test. I've already spent over an hour getting to this point (there was no one on the ask a question line), so I figure raising an issue is the best solution.

Tests fail for categories/edit and posts/edit

After following the lesson the tests fail similarly to the last lab, generating the errors:

Failures:

  1. categories/edit shows an update form that submits content, redirects, and shows the updated content
    Failure/Error: category.update(params.require(:category))
    ActiveModel::ForbiddenAttributesError:
    ActiveModel::ForbiddenAttributesError

  2. posts/edit shows an update form that submits content, redirects, and shows the updated content
    Failure/Error: post.update(params.require(:post))
    ActiveModel::ForbiddenAttributesError:
    ActiveModel::ForbiddenAttributesError

These appear to be caused by issues with the category and posts controller.

The category controller will pass the spec tests if .permit! is added after the update call call

Previously:
def update
category = Category.find(params[:id])
category.update(params.require(:category))
redirect_to category_path(category)
end
Passing:
def update
category = Category.find(params[:id])
category.update(params.require(:category).permit!)
redirect_to category_path(category)
end

The same can be done for the posts controler:
Previously:
def update
@post = Post.find(params[:id])
@post.update(params.require(:post))
redirect_to post_path(@post)
end
Passing:
def update
@post = Post.find(params[:id])
@post.update(params.require(:post).permit!)
redirect_to post_path(@post)
end

Im assuming this was not the intended function given they are not addressed in the lesson and it appears to be a codealong.

"data-visibility='hidden'" showing learn.co (also links to self)

The link at the bottom of the page

screen shot 2016-01-16 at 1 52 19 pm

<a href='https://learn.co/lessons/displaying-associations-rails'
data-visibility='hidden'>View this lesson on Learn.co</a>

I did some research, and it looks like 'data-visibility' is relaed to javascript, which I know nothing about. I didn't change it myself, because I don't want to mess-up something on the page. If "data-visibility='hidden'" can be deleted, I'll be happy to go and do that.

ForbiddenAttributesError

posts_controller.rb and categories_controller.rb are missing private strong params methods causing two tests to fail.

  1. posts/edit shows an update form that submits content, redirects, and shows the updated content
    Failure/Error: post.update(params.require(:post))

    ActiveModel::ForbiddenAttributesError:
    ActiveModel::ForbiddenAttributesError


I was able to fix with the following :

posts_controller:

def update
	post = Post.find(params[:id])
	post.update(post_params)
	redirect_to post_path(post)
end

private

def post_params
	params.require(:post).permit(:title, :description)
end

categories_controller.rb

def update
category = Category.find(params[:id])
category.update(category_params)
redirect_to category_path(category)
end

private

def category_params
params.require(:category).permit(:name)
end
end

Possible error in the tests.

In the spec/features/post_spec.rb file, I think the line:

@post.category = Category.create!(name: "Cats")

... should be added (on row 6). Otherwise, the code:

<%# app/views/posts/show.html.erb %>

<%= @post.title %>

Category: <%= link_to @post.category.name, category_path(@post.category) %>

<%= @post.description %>

... breaks because the post does'nt have a category. The error I get running bundle exec rspec is:

  1. navigate shows the title on the show page in a h1 tag
    Failure/Error: Category: <%= link_to @post.category.name, category_path(@post.category) %>
 ActionView::Template::Error:
   undefined method `name' for nil:NilClass

Best,
Einar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.