rails undefined method `reviewer' for #<Review - ruby-on-rails

I'm creating review to my posts, all works but i dont know how to show who wrote the review
i'm trying this:
class User < ActiveRecord::Base
has_many :posts
has_many :reviewers, :class_name => 'Post', :foreign_key => 'reviewer_id'
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :reviews
belongs_to :reviewers, class_name: 'User', :foreign_key => 'reviewer_id'
default_scope -> { order('created_at DESC') }
end
class Review < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
class ReviewsController < ApplicationController
def new
#post = Post.find(params[:post_id])
#review = #post.reviews.new(post_id:params[:post_id])
end
def create
#post = Post.find(params[:post_id])
#review = #post.reviews.build(review_params)
if #review.save
flash[:success] = "Ваш отзыв добавлен"
redirect_to post_path #post
else
render 'new'
end
end
private
def review_params
params.require(:review).permit(:post_id, :body, :reviewer_id).merge(:reviewer_id => current_user.id)
end
end
and my view
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.reviewer.email %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>
my migration
class CreateReviews < ActiveRecord::Migration
def change
create_table :reviews do |t|
t.text :body
t.references :post, index: true
t.references :reviewer, index: true
t.timestamps
end
end
end
but rails given error undefined method `reviewer' for #
Help please dsfsdf

I think that you have a pluralization issue:
A post have many reviews by many reviewers (one for each review). But you are storing the foreign key within the post so you written the problematic line:
belongs_to :reviewers, class_name: 'User', :foreign_key => 'reviewer_id'
The issue here is that it is a singular association with a plural name.
I think that you are trying to say here that a
class Post
have_many :reviewers, class_name: 'User', through: :reviews
end
But as you are trying to access the reviewers from the review what you really need is to add:
class Review
belongs_to :reviewer, class_name: 'User'
end
Then you can access the reviewers from the review as expected:
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.reviewer.email %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>
There is also an error in User:
has_many :reviewers, :class_name => 'Post', :foreign_key => 'reviewer_id'
As it should be:
has_many :reviews, :foreign_key => 'reviewer_id'

You need to be using .user. Check the belongs_to in your model.
Review.first.user

As a previous poster pointed out, your user association for Review is :user, so your view should probably look like this:
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.user.name unless review.user.nil? %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>

Related

Attributes of referenced user are all nil

I have a post that can accept comments and those comments belong to the user and the post. I can successfully create a comment and it will be associated with the post and the user (checked that by going into console, the user_id and post_id is there), but when I try to call a user's attribute (such as user.username) it does not work. If I call a post's attribute (such as post.body) it works. When making the comments model I have referenced the user and post in my migration. In my user.rb I have also tried using has_many :comments, through: :posts but that did not work.
user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
end
comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :findpost
def create
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to post_path(#post)
else
flash[:alert] = "Check the comment form"
end
end
def comment_params
params.require(:comment).permit(:body)
end
private
def findpost
#post = Post.find(params[:post_id])
end
end
The migration
class CreateComments < ActiveRecord::Migration[6.0]
def change
create_table :comments do |t|
t.string :body
t.references :user, null: false, foreign_key: true
t.references :post, null: false, foreign_key: true
t.timestamps
end
end
end
The specific error I get
undefined method `username' for nil:NilClass
Where comment.user.username is being called
<%= render partial: 'comments/form' %>
<% if #post.comments.count > 0 %>
<%= #post.comments.each do |comment| %>
<div>
<%= link_to author_path(id: comment.user.username) do %>
<%= comment.user.username %>
<p><%= comment.body %></p>
</div>
<% end %>
<% end %>
This error is at comment.user.username. The main problem is that whatever attribute that I call for comment.user it always returns nil (even something like id).

How to get user name of Liker?

The example line in here didn't work. I tried 20 varieties of it.
views/comments/_comments.html.erb
<% #comments.each do |comment| %>
<%= User.find(comment.user_id).name %> # Gives user name of commenter
<%= simple_format comment.content %>
<%= pluralize(comment.likes, 'like') %>
<%= link_to content_tag(:span, '', class: 'glyphicon glyphicon-thumbs-up') +
' Like it', like_comment_path(:id => comment.id), method: :post %>
<%= User.find(like.user_id).name %> # How to get user name of liker?
<% end %>
comments_controller.rb
def like
#comment = Comment.find(params[:id])
comment_like = current_user.comment_likes.build(comment: #comment)
if comment_like.save
#comment.increment!(:likes)
#comment.create_activity :like
#user = User.find(params[:id])
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
comment_like.rb
class CommentLike < ActiveRecord::Base
belongs_to :comment
belongs_to :user
validates :user, uniqueness: { scope: :comment }
end
comment.rb
class Comment < ActiveRecord::Base
include PublicActivity::Common
# tracked except: :update, owner: ->(controller, model) { controller && controller.current_user }
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User'
belongs_to :commentable, polymorphic: true
belongs_to :user
end
A comment has many likes. So you are looking for the comment likers.
class Comment
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User', source: :liker
end
class CommentLikers
belongs_to :liker, class_name: 'User', foreign_key: :user_id
belongs_to :liked_comment, class_name: 'Comment', foreign_key: :comment_id
end
class User
has_many :comment_likes
has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
end
Then in your views:
<% #comments.each do |comment| %>
<% comment.likers.each do |user| %>
<%= user.name %>
<% end %>
<% end %>
EDIT 1
Your question is so well explained in the rails guides:
http://guides.rubyonrails.org/association_basics.html
But just to give you a short explanation:
You need to associate the comments through the comment likes to the users. To do you need to tell rails to look for the user in the table using the user_id.
That's what the through: :comment_likes, class: 'User' do.
EDIT 2
Check the source code to see more options:
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
1) If like variable declared in controller it should be global: #like
2) Don't use User.find(comment.user_id).name or User.find(like.user_id).name.
Use it like comment.user.name, #like.user.name
The short answer
In your controller, change
comment_like = current_user.comment_likes.build(comment: #comment)
if comment_like.save
with
#comment_like = current_user.comment_likes.build(comment: #comment)
if #comment_like.save
In your view, change
<%= User.find(like.user_id).name %>
with
<%= #comment_like.user.name %>

Rails: Creating form fields for join-table dependent on own model

I'm trying to build a form for a model with a join-table containing references to itself.
I'll give you a quick example of what i'm trying to achive:
Lets say i have the subject physics. To learn physics you will need to know basic math, i.e physics is dependent on math. Any subject should be able to have multiple dependencies both ways.
What i'm having problems with is submitting this relationship through a form.
My code:
Model:
class Subject < ActiveRecord::Base
has_many :needs, foreign_key: :target_id
has_many :subjects, through: :needs
accepts_nested_attributes_for :needs,:subjects,allow_destroy: true
end
class Need < ActiveRecord::Base
belongs_to :target, class_name: :subject
belongs_to :prerequisite, class_name: :subject
end
Controller:
class SubjectsController < ApplicationController
def create
#subject = Subject.new secure_params
if #subject.save
redirect_to root_path, success: 'Subject created'
else
render :new
end
end
def new
#subject = Subject.new
end
private
def secure_params
params.require(:subject).permit(:name, :content, needs_attributes: [:target,:prerequisite])
end
end
Form:
<div class="row">
<div class="col-md-8">
<%= simple_nested_form_for #subject do |f| %>
<%= f.input :name %>
<%= f.input :content %>
<%= f.fields_for :needs do |d| %>
<%=d.association :prerequisite %>
<% end %>
<%= f.link_to_add "Add a prerequisite", :needs %>
<%= f.submit class: 'btn btn-primary btn-lg' %>
<% end %>
</div>
</div>
Using this approach i get "uninitialized constant Need::subject" on entering the "new" action.
Any ideas on mistakes in my approach or code will be greatly appreciated.
Edit: adding join table
class CreateNeeds < ActiveRecord::Migration
def change
create_table :needs do |t|
t.references :target
t.references :prerequisite
end
end
end
Edit2: working code (only changed parts)
Model
class Subject < ActiveRecord::Base
has_many :needs, foreign_key: :target_id
has_many :prerequisites, class_name: "Subject",through: :needs, source: :prerequisites
accepts_nested_attributes_for :needs,:prerequisites,allow_destroy: true
end
class Need < ActiveRecord::Base
belongs_to :target, class_name: "Subject"
belongs_to :prerequisite, class_name: "Subject"
end
Controller
def secure_params
params.require(:subject).permit(:name, :content, needs_attributes: [:prerequisite_id])
end
end
Like i said in my comment, focus on making sure all the associations are hooked up properly before you worry about forms. The console, or automated tests, are both a good place to test this.
I think it maybe should be like so:
class Subject < ActiveRecord::Base
has_many :needs, as: :target
has_many :subjects, :class_name: "Subject", through: :needs, source: :prerequisite
accepts_nested_attributes_for :needs,:subjects,allow_destroy: true
end
#assuming you have fields target_id and prerequisite_id in this table
class Need < ActiveRecord::Base
belongs_to :target, class_name: :subject
belongs_to :prerequisite, class_name: :subject
end
I think the association name "subjects" is potentially confusing as it will return the subjects that this subject requires. This isn't immediately obvious to the reader, which could include you in the future.
At some point you may want to list the subjects that require this subject. It may be worth differentiating between these now, so you can clearly tell the difference. You could do this like so:
class Subject < ActiveRecord::Base
has_many :required_needs, as: :target
has_many :required_by_needs, as: :prerequisite
has_many :required_subjects, :class_name: "Subject", through: :required_needs, source: :prerequisite
has_many :required_by_subjects, :class_name: "Subject", through: :required_by_needs, source: :target
accepts_nested_attributes_for :required_needs, :required_by_needs, :required_subjects, :required_by_subjects, allow_destroy: true
end

Polymorphic Nested Form

I'm having issue creating a nested polymorphic form. I am following the solution from this problem:
Rails: has_many through with polymorphic association - will this work?
The description was: A Person can have many Events and each Event can have one polymorphic Eventable record
Here are the relevant models:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable, :polymorphic => true
end
class Meal < ActiveRecord::Base
has_one :event, :as => eventable
end
class Workout < ActiveRecord::Base
has_one :event, :as => eventable
end
class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
end
My controller looks like this:
def
#person = Person.new
#person.meals.new
#person.workouts.new
new
My view looks like this:
<%= form_for #person do |person| %>
<%= person.fields_for :meals, #person.meals.build do |meals| %>
<%= meals.text_field :food %>
<% end %>
<% end %>
The error I am currently getting is:
unknown attribute: person_id
I would think that the polymorphic association is hindering the creation of the object because meals can't be create until person has been created? Did I set up the form correctly? Thanks!
You'll need to include a person_id attribute in the events table (as per the belongs_to association)
There are also some other issues you should resolve:
Controller
def new
#person = Person.build
end
def create
#person = Person.new(person_attributes)
#person.save
end
private
def person_attributes
params.require(:person).permit(:your, :attributes, events_attributes: [:eventable_type, :eventable_id, meal_attributes: [:params], workout_attributes: [:params]])
end
Models
#app/models/person.rb
Class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
def self.build
person = Person.new
person.events.build.build_meal
person.events.build.build_workout
end
end
Views
#app/views/people/new.html.erb
<%= form_for #person do |person| %>
<%= person.fields_for :events do |e| %>
<%= e.fields_for :meal %>
<%= e.text_field :food %>
<% end %>
<% end %>
<% end %>

How to create a new nested resource?

A users can have many favorites top_songs,top_movies through songs and movies table.
A user registered user(current_user) want to post his favorites movies and songs.
Perhaps all Model association are right, i am stuck in controller and view (form).
When i submit from, i gets errors-
Can't mass-assign protected attributes: songs
How can i achieve this please?
all codes are below.
User Model
class User < ActiveRecord::Base
attr_accessible :id, :name_special_char, :screenname, :fullname, :username, :prefix, :firstname, :lastname,:middlename, :suffix, :age, :sex, :email,
:top_movies_attributes,:top_songs_attributes
has_many :top_movies
has_many :movies, through: :top_movies
has_many :top_songs
has_many :songs, through: :top_songs
accepts_nested_attributes_for :top_songs, :allow_destroy => true
accepts_nested_attributes_for :top_movies, :allow_destroy => true
end
Movie Model
class Movie < ActiveRecord::Base
attr_accessible :name
has_many :top_movies
has_many :users, through: :top_movies
end
TopMovie Model
class TopMovie < ActiveRecord::Base
belongs_to :user
belongs_to :movie
# attr_accessible :title, :body
end
Song Model
class Song < ActiveRecord::Base
attr_accessible :name
has_many :top_songs
has_many :users, through: :top_songs
end
TopSong Model
class TopSong < ActiveRecord::Base
belongs_to :user
belongs_to :song
# attr_accessible :title, :body
end
Controller
class MyTopFivesController < ApplicationController
def new
#favorites = current_user
#favorites=#favorites.movies.build()
#favorites=#favorites.songs.build()
respond_to do |format|
format.html # new.html.erb
format.json { render json: #useraccounts_my_top_fife }
end
end
def create
#favorites = current_user(params[:user])
#favorites.save!
# Here i have stuck. i am not sure how to save.
end
view form
<%=nested_form_for #favorites ,:url=>favorites_path(#favorites),:method=>'post' do |f| %>
<label >Songs</label>
<%= f.fields_for :songs do |songs| %>
<div id="Topsongs" >
<div class="input-control text span5 place-left ">
<%= songs.text_field :name,:placeholder=>"songs name.." %>
</div>
<div class="span1 place-left">
<%= songs.link_to_remove "", :class=>"icon-minus" %>
</div>
</div>
<% end %>
<span >
<%= f.link_to_add "", :songs, :class=>"icon-plus", :data => { :target => "#Topsongs" } %>
</span>
<label >movies</label>
<%= f.fields_for :movies do |movies| %>
<div id="Topmovies">
<div class="input-control text span5 place-left ">
<%= movies.text_field :name,:placeholder=>"movies name.." %>
</div>
<div class="span1 place-left">
<%= movies.link_to_remove "", :class=>"icon-minus" %>
</div>
</div>
<% end %>
<span>
<%= f.link_to_add "", :movies, :class=>"icon-plus",:style=>"font-size: 14px;", :data => { :target => "#Topmovies" } %>
</span>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
There are two options for setting up the associations here. One you have used by creating two join models for top songs and top movies. And other, to use polymorphic association.
Lets use polymorphic association. We are going to use User, Movie, Song and Favourite models for this stuff. The Favourite model will contain the polymorphic fields.
User.rb
class User < ActiveRecord::Base
attr_accessible :id, :name_special_char, :screenname, :fullname, :username, :prefix, :firstname, :lastname,:middlename, :suffix, :age, :sex, :email
has_many :favourites
has_many :movies, through: :favourites, source: :favouritable, source_type: 'Movie'
has_many :songs, through: :favourites, source: :favouritable, source_type: 'Song'
end
Movie.rb
class Movie < ActiveRecord::Base
attr_accessible :name
has_many :favourites, as: :favouritable
has_many :users, through: :favourites
end
Song.rb
class Song < ActiveRecord::Base
attr_accessible :name
has_many :favourites, as: :favouritable
has_many :users, through: :favourites
end
Favourite.rb
class Favourite < ActiveRecord::Base
belongs_to :users
belongs_to :favouritable, polymorphic: true
end
We also need to create the migration for new model "Favourite". As of now, we just need 3 columns ie. user_id, favouritable_id, favouritable_type. Here favouritable_type and favouritable_id are the polymorphic fields. favouritable_type is a string and favouritable_id is reference type.
Migration File
class CreateFavourites < ActiveRecord::Migration
def change
create_table :favourites do |t|
t.integer :user_id
t.references :favouritable, polymorphic: true
t.timestamps
end
end
end
Now, as we are going to mark some movies and songs to be favourite for a user, then we can place the code for building the data in UsersController instead of creating another controller or we can also create a controller for Favourites. I am going to use UsersController here. I am using update action to update the favourites as we don't need any extra functionality here. You can add a new action if you want.
In UsersController.rb
def edit_favourites #or some generic name
#user = current_user.includes(:movies, :songs)
#movies = Movie.all
#songs = Song.all
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to users_path #user index page
else
if params[:user][:movie_ids].present? or params[:user][:song_ids].present?
render :edit_favourites
else
render :edit
end
end
end
edit_favourites.html.erb
<%= form_for(#user) do |f| %>
<div class="fields">
<%= f.label :movie_ids, "Favourite Movies: " %>
<%= f.collection_select :movie_ids, #movies, :id, :name, {}, multiple: true %>
</div>
<div class="fields">
<%= f.label :song_ids, "Favourite Songs: " %>
<%= f.collection_select :song_ids, #songs, :id, :name, {}, multiple: true %>
</div>
<% end %>
Also, Add the new action to routes.

Resources