Polymorphic association: NoMethodError - ruby-on-rails

NoMethodError in PostsController#create
undefined method `posts' for nil:NilClass
Posts#controller
class PostsController < ApplicationController
# before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
before_action :find_postable, except: [:index]
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new
end
def create
#post = #postable.posts.new post_params
if #post.save
redirect_to :back, notice: 'Your comment was successfully posted!'
else
redirect_to :back, notice: "Your comment wasn't posted!"
end
end
def destroy
Post.find(params[:id]).destroy
redirect_to posts_path#, notice: "Post successfully deleted"
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(student_params)
redirect_to post_path
else
redirect_to edit_post_path
end
end
def show
#post = Post.find (params[:id])
end
private
def post_params
params.require(:post).permit(:content, :image, :document, :audio, :video, :classroom_id)
end
def correct_user
#post = current_user.posts.find_by(id: params[:id])
redirect_to root_url if #post.nil?
end
def find_postable
#postable = Post.find_by_id(params[:post_id]) if params[:post_id]
#postable = Classroom.find_by_id(params[:classroom_id]) if params[:classroom_id]
end
Here is the post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :classroom
belongs_to :postable, polymorphic: true
has_many :posts, as: :postable
end
Here is the classroom model (it belongs to the class and this is selected in the new post form"
class Classroom < ActiveRecord::Base
has_many :posts, as: :postable
belongs_to :teachers
# belongs_to :students
Posts in Schema.rb
create_table "posts", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "classroom_id"
t.integer "postable_id"
t.string "postable_type"
end
I've tried many things with no success, I'm not quite sure why I'm getting this error. I'm following a video and trying to apply it to my existing post model but am still having trouble. Thanks
New Post Form:
<%= form_for :post, html: { multipart: true, class: "post-form" }, url: posts_path do |f| %>
<h4>Choose a Classroom</h4>
<div class="form-group">
<%= f.collection_select :classroom_id, current_user.classrooms, :id, :name %>
</div>
<h4>Write a post</h4>
<div class="form-group">
<%= f.text_area :content, class: "form-control" %>
</div>
<h4>Attach Files</h4>
<div class="form-group">
<%= f.file_field :image %>
</div>
<%= f.submit "Add Post", class: "btn btn-primary" %>
<% end %>
For a post reply
<li>
<%= post.content %> -
<%= form_for [post, Post.new] do |f| %>
<%= f.text_area :content, placeholder: "Add a Reply" %><br/>
<%= f.submit "Reply" %>
<% end %>
</li>

The error is in find_postable method, specifically in this line:
#postable = Classroom.find_by_id(params[:classroom_id]) if params[:classroom_id]
You are trying to fetch classroom_id from params (i.e. params[:classroom_id]) but that key (classroom_id) exists within post key in your parameters hash:
Parameters: {
"utf7"=>"✓",
"authenticity_token"=>"bunchofletters==",
"post"=>{
"classroom_id"=>"1",
"content"=>"hello"
},
"commit"=>"Add Post"
}
To fix this, change params[:classroom_id] to params[:post][:classroom_id] in find_postable method:
def find_postable
#postable = Post.find_by_id(params[:post_id]) if params[:post_id]
#postable = Classroom.find_by_id(params[:post][:classroom_id]) if params[:classroom_id]
end

Related

Rails reviews not showing up on album show page

Albums have many reviews and users through reviews, Reviews belong to album and belong to user, and Users have many reviews and have many albums through reviews.
On my albums show page, Reviews do not display. But Reviews display on the reviews index page. I can't seem to figure out what I'm missing. I'm not getting any errors.
Here is my Albums controller:
class AlbumsController < ApplicationController
before_action :set_album, only: [:show, :edit, :update]
def index
#albums = Album.all
#current_user
end
def show
#review = #album.reviews.build
end
def new
#album = Album.new
end
def create
#album = Album.new(album_params)
if #album.save
redirect_to album_path(#album)
else
render :new
end
end
def edit
end
def update
if #album.update(album_params)
redirect_to album_path(#album), notice: "Your album has been updated."
else
render 'edit'
end
end
private
def set_album
#album = Album.find(params[:id])
end
def album_params
params.require(:album).permit(:artist, :title, :avatar)
end
end
Reviews Controller:
class ReviewsController < ApplicationController
def index
#reviews = Review.all
end
def show
#review = Review.find(params[:id])
##reviews = Review.where("album_id = ?", params[:album_id])
end
def new
#review = Review.new
end
def create
#review = current_user.reviews.build(review_params)
if #review.save
redirect_to reviews_path
else
render :new
end
end
def update
end
private
def review_params
params.require(:review).permit(:title, :date, :content, :user_id, :album_id, album_attributes:[:artist, :title])
end
end
Here is my albums show page:
<% if #album.avatar.attached? %>
<image src="<%=(url_for(#album.avatar))%>%" style="width:350px;height:350px;">
<% end %>
<br>
<%= #album.artist %> -
<%= #album.title %>
<br>
<%= link_to "Edit Album", edit_album_path %>
<br><br><br>
<%= render '/reviews/form' if logged_in? %>
<h3>Album Reviews</h3>
<% #album.reviews.each do |review| %>
<%= review.title %>
<%= review.content %>
<% end %>
<br><br><br>
<%= link_to "All Albums", albums_path %><br>
<%= link_to "Upload a New Album", new_album_path %>
Here is my routes file:
Rails.application.routes.draw do
get '/signup' => 'users#new', as: 'signup'
post '/signup' => 'users#create'
get '/signin' => 'sessions#new'
post '/signin' => 'sessions#create'
get '/signout' => 'sessions#destroy'
resources :albums do
resources :reviews, except: [:index]
end
resources :users
resources :reviews, only: [:index]
root to: "albums#index"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
Here are my Models:
class Album < ApplicationRecord
has_many :reviews
has_many :users, through: :reviews
has_one_attached :avatar
end
class Review < ApplicationRecord
belongs_to :album, optional: true
belongs_to :user
accepts_nested_attributes_for :album
end
class User < ApplicationRecord
has_secure_password
has_many :reviews
has_many :albums, through: :reviews
accepts_nested_attributes_for :reviews
end
Review form:
<%= form_for :review, method: "POST",:url => { :action => :create } do |f| %>
<!-- <%= f.collection_select :album_id, Album.order(:artist),:id,:artist %> -->
<br>
<%= f.label :title %>
<%= f.text_field :title%>
<br><br>
<%= f.label :date %>
<%= f.datetime_field :date%>
<br><br>
<%= f.label :content %>
<%= f.text_area :content %>
<br><br>
<%= f.submit "Submit" %>
<% end %>
The problem is the iteration, because you use the instance variable coming from the controller instead of the block variable, it should be:
<% #album.reviews.each do |review| %>
<%= review.title %>
<%= review.content %>
<% end %>
#review is a new instance of a review (empty) which you created for the form I guess.
So instead of showing each review's title and content you showed the one from the empty review saved in #review in each iteration.

ActionView::Template::Error (uninitialized constant ActionView::CompiledTemplates::Cflags

I have a very basic Photo and Comments model that works and then I have a built a Cflags model that is used to flag comments. I am getting the following error from Heroku log when I visit the photos/show.html.erb view.
ActionView::Template::Error (uninitialized constant ActionView::CompiledTemplates::Cflags
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflags.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
resources :photos do
resources :comments do
resources :cflags
end
end
PhotosController
def show
#photo = Photo.approved.find(params[:id])
end
CommentsController
def create
#photo = Photo.find(params[:photo_id])
#comment = #photo.comments.build(comment_params)
#comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
class Cflag < ActiveRecord::Base
belongs_to :comment, counter_cache: true
belongs_to :user, counter_cache: true
validates :user_id, presence: true
validates :comment_id, presence: true
validates :user_id, uniqueness: {
scope: [:comment_id],
message: 'You can only flag a comment once. Thank you for your feedback.'
}
default_scope -> { order(created_at: :desc) }
end
class CflagsController < ApplicationController
before_action :logged_in_user
def new
end
def create
#comment = Comment.find(params[:comment_id])
#cflag = #comment.cflags.build(cflag_params)
if #cflag.save
if #comment.cflags_count > 1
#comment.update_attribute(:approved, false)
flash[:success] = "Flag created! Comment ##{#comment.id} has been removed for review. Thank you for your feedback"
redirect_to :back
else
flash[:success] = "Flag created! Thank you for your feedback"
redirect_to :back
end
else
redirect_to :back, notice: #cflag.errors.full_messages
end
end
private
def cflag_params
params.require(:cflag).permit(:user_id, :comment_id).merge(user_id: current_user.id)
end
end
create_table "cflags", force: :cascade do |t|
t.integer "comment_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "cflags", ["comment_id"], name: "index_cflags_on_comment_id"
add_index "cflags", ["user_id"], name: "index_cflags_on_user_id"
You have a typo. You model is called Cflag not Cflags. Change this:
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflags.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
to this
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflag.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>

Rails: undefined method `relationships_path'

I was following the Michael Hart book and wanted to and add the follow and unfollow button to the users show page. While implementing this I encountered this error undefined method 'relationships_path'.
Any help would be helpful and appreciated.
relationships controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
users controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user
def create
#user = User.friendly.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
Relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
User.rb
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :followers, through: :passive_relationships, source: :follower
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
end
_follow.html.erb
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
<div><%= hidden_field_tag :followed_id, #user.id %></div>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
_unfollow.html.erb
<%= form_for(current_user.active_relationships.find_by(followed_id: #user.id),
html: { method: :delete },
remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="stats">
<% #user ||= current_user %>
<div class="stats">
<strong id="following" class="stat">
<%= #user.following.count %>
</strong>
following
</a>
<strong id="followers" class="stat">
<%= #user.followers.count %>
</strong>
followers
</a>
<%= render 'follow' %>
</div>
</section>
</aside>
</div>
Routes.rb
Rails.application.routes.draw do
get 'users/index'
get 'users/show'
devise_for :users do
member do
get :following, :followers
end
end
get 'users/:id' => 'users#show', as: :user
end
Relationships | Migration
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id, index: true
t.integer :followed_id, index: true
t.timestamps null: false
end
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
As per your route.rb, you don't have the required route i.e. POST /relationships. Add it into your route.rb:
resources :relationships

Having trouble rendering checkboxes in a Rails 4 many_to_many association

I have a many_to_many association between Categories and Articles, using has_and_belongs_to_many in a Rails 4 app:
Here are the corresponding migration and classes:
class CategoriesArticles < ActiveRecord::Migration
def change
create_table :categories_articles, id: false do |t|
t.belongs_to :category, index: true
t.belongs_to :article, index: true
end
add_index :categories_articles, [:category_id, :article_id]
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
end
class Article < ActiveRecord::Base
has_and_belongs_to_many :categories
end
When a user creates a new article, I simply want to give him or her the option to select categories that he/she wants to associate with the new article. I want the user to be able to select these categories with checkboxes.
Here's the ArticlesController:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :create, :edit, :destroy, :update]
before_action :verify_own_article, only: [:destroy]
respond_to :html
def index
if user_signed_in?
#articles = current_user.article_feed
# TODO: if there are too few articles on a user's feed, we want to display more articles
else
#articles = Article.all
end
#articles = #articles.page(params[:page] || 1).per(12)
respond_with(#articles)
end
def show
respond_with(#article)
end
def new
#categories = Category.all
#article = Article.new
respond_with(#article)
end
def edit
if current_user.articles.find_by_id(params[:id]).nil?
flash[:notice] = "You do not have permission to edit this article."
redirect_to #article
end
end
def create
# Creates article object with current_user_id, initial_comment, and URL
#article = current_user.articles.build(article_params)
# Uses Pismo (gem) to grab title, content, photo of URL
#article.populate_url_fields
if #article.save
flash[:success] = "Article created!"
# Might need to change the location of this redirect
redirect_to root_url
else
flash[:notice] = "Invalid article."
redirect_to new_article_path
end
end
def update
#article.update(article_params)
flash[:notice] = "Article successfully updated."
respond_with(#article)
end
def destroy
if #article
#article.destroy
flash[:notice] = "Article successfully destroyed."
else
flash[:notice] = "You do not have permission to delete this article."
end
# TODO: change this to another redirect location
redirect_to root_path
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:url, :title, :datetime, :content, :photo, :initial_comment)
end
# Ensure that a signed in user can only delete articles that they have posted
def verify_own_article
#article = current_user.articles.find_by_id(params[:id])
end
end
Here's the article new.html.erb view:
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
... and the form partial:
<%= form_for(#article) do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% #article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br>
<%= f.text_field :url %>
</div>
<div class="field">
<%= f.label :initial_comment %><br>
<%= f.text_field :initial_comment %>
</div>
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, this is erroring for me, specifically the lines:
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
Specifically, it's telling me:
undefined method 'categories[1]' for #<Article:0x007f401193d520>. How do I fix this? Thanks.
I note your join table is named
create_table :categories_articles, id: false do |t|
The name needs to be in alphabetical order.
create_table :articles_categories, id: false do |t|
My pick is that Rails cannot find your join table. This means that the form when it calls #article.categories cannot find what it needs. The same will happen if you call #category.articles (ie: being able to find the join table is not related to the arbitrary order of the objects).
See my answer to this question

Each time I edit a Post/Reply nested form, a new reply is being added?

I have a nested form which consists on a post and replies:
posts_controller.rb:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def show
end
def new
#post = Post.new
#post.replies.build
end
.
.
.
private
def set_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:content, :user, replies_attributes: [:content])
end
end
replies_controller.rb:
class RepliesController < ApplicationController
before_action :set_reply, only: [:show, :edit, :update, :destroy]
def new
#reply = Reply.new
end
def edit
end
def create
#reply = Reply.new(reply_params)
respond_to do |format|
if #reply.save
format.html { redirect_to #reply, notice: 'Reply was successfully created.' }
format.json { render action: 'show', status: :created, location: #reply }
else
format.html { render action: 'new' }
format.json { render json: #reply.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #reply.update(reply_params)
format.html { redirect_to #reply, notice: 'Reply was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #reply.errors, status: :unprocessable_entity }
end
end
end
private
def set_reply
#reply = Reply.find(params[:id])
end
def reply_params
params.require(:reply).permit(:content)
end
end
post.rb:
class Post < ActiveRecord::Base
has_many :replies, :dependent => :destroy
accepts_nested_attributes_for :replies, :allow_destroy => true
end
reply.rb:
class Reply < ActiveRecord::Base
belongs_to :post
end
posts/_reply_fields.html.erb:
<p>
<%= f.label :content, "Reply" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
posts/_form.html.erb:
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :content %><br>
<%= f.text_field :content %>
</div>
<div class="field">
<%= f.label :user %><br>
<%= f.text_field :user %>
</div>
<div class="replies">
<%= f.fields_for :replies do |builder| %>
<%= render 'reply_fields', :f => builder %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
schema.rb:
create_table "posts", force: true do |t|
t.string "content"
t.string "user"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "replies", force: true do |t|
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_id"
end
The output in the replies parts is just:
<div class="replies">
</div>
Now, everything works OK if I submit the form: a new post and reply is created. But each time I edit a post a new reply is created.
So if I edit a post like this:
post title: Post Title 1
post content: Post Content 1
reply content: Reply Content 1
I end up with something like this:
post title: Post Title 1
post content: Post Content 1
reply content: Reply Content 1
reply content: Reply Content 1
and on and on and on...
I want could be the problem?
In your posts/_form.html.erb the submit path is #post which is fine for creating new record,for editing you need to have path edit_post_path which sends the post id for updating. You can run the command rake routes to check the paths for corresponding actions.

Resources