I am building a book review application from on online tutorial. I am trying to save a book review with a form. I have a book table and a reviews table. If the book review saves, I've told the review form to redirect to the book show page. If not, render 'new' again. I get zero errors when I try to save. It just puts me back on the new review page. I went into the console, and the reviews are not being saved. I don't know what's going on. Can someone help?
Here is my books controller:
class BooksController < ApplicationController
before_action :authenticate_user!, only: [:new, :edit, :index, :show]
def index
#books = Book.all
end
def new
#book = current_user.books.new
end
def create
#book = current_user.books.build(book_params)
if #book.save
redirect_to books_path
else
render 'new'
end
end
def show
#book = Book.find(params[:id])
end
def edit
#book = Book.find(params[:id])
end
def update
#book = Book.find(params[:id])
if #book.update(book_params)
redirect_to book_path(#book)
else
render "edit"
end
end
def destroy
#book = Book.find(params[:id])
#book.destroy
if #book.destroy
redirect_to books_path
else
render 'show_books_path'
end
end
private
def book_params
params.require(:book).permit(:title, :description, :author, :category_id, :book_img)
end
end
Here is my reviews controller:
class ReviewsController < ApplicationController
before_action :find_book
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.book_id = #book.id
#review.user_id = current_user.id
if #review.save
redirect_to book_path(#book)
else
render 'new'
end
end
def edit
end
def update
end
def destroy
end
private
def review_params
params.require(:review).permit(:rating, :comment)
end
def find_book
#book = Book.find(params[:book_id])
end
end
Here is my review model:
class Review < ApplicationRecord
belongs_to :books
belongs_to :users
end
Here is my book model:
class Book < ApplicationRecord
belongs_to :user
has_many :reviews
has_attached_file :book_img, styles: { book_index: "250x350>", book_show: "400x600>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :book_img, content_type: /\Aimage\/.*\z/
end
I'm using a devise form. Here is what I have:
<%= simple_form_for([#book, #book.reviews.build]) do |f| %>
<p>Rating</p>
<%= f.input :rating, label: false, :class => "input" %>
<p>Comment</p>
<%= f.text_area :comment, label: false, :class => "input" %>
<%= f.button :submit, :class => "submit" %>
<% end %>
Here is my routes file:
Rails.application.routes.draw do
devise_for :users
resources :books do
resources :reviews
end
root "books#index"
end
I'm really not sure what's going on here. When I go into the console, the reviews are not being saved. Eventually, I want to display them, but I haven't gotten to that step. Any help would be very appreciated!
I see that in your Review model you have this:
class Review < ApplicationRecord
belongs_to :books
belongs_to :users
end
When it should be like this:
class Review < ApplicationRecord
belongs_to :book
belongs_to :user
end
belongs_to associations have to use the singular term
Related
On my homepage, I'm trying to set it up so when you click the "Get started" button, a website record is created, but also a page belonging to that website is created, and you're redirected to the page.
This is what I have so far. The website record is being created but the page is not being created.
Models
class Page < ApplicationRecord
belongs_to :website
end
class Website < ApplicationRecord
has_many :pages, :dependent => :destroy
accepts_nested_attributes_for :pages
end
Homepage controller
class MarketingPagesController < ApplicationController
def home
#website = Website.new
#website.pages.build
end
end
Website controller
class WebsitesController < ApplicationController
def create
#website = Website.new(creation_params)
if #website.save
redirect_to #website.Page.first
else
render :new
end
end
private
def shared_params
[:name]
end
def creation_params
params.require(:website).permit(*shared_params)
end
def update_params
params.require(:website).permit(*shared_params)
end
end
Page Controller
class PagesController < ApplicationController
def create
#page = Page.new(creation_params)
if #page.save
redirect_to #page
else
render :new
end
end
def show
#page = Page.find(params[:id])
#templates = Template.all
end
private
def shared_params
[:name, :website_id]
end
def creation_params
params.require(:page).permit(*shared_params)
end
def update_params
params.require(:page).permit(*shared_params)
end
end
Website form on homepage
<%= form_for #website do |f| %>
<%= f.hidden_field :name, value: "Untitled site" %>
<%= f.fields_for :pages do |builder| %>
<%= builder.hidden_field :name, value: "Untitled page" %>
<% end %>
<%= f.submit "Create Website" %>
<% end %>
You are using the association incorrectly
# Change
#website.Page.first
# to
#website.pages.first
Change this snippet in WebsiteController
if #website.save
redirect_to #website.pages.first
else
render :new
end
you have not white listed page params in website controller. modify your shared_params in website controller to :
def shared_params
[:name, pages_attributes: [:id, :name]]
end
and of course do changes suggested by #Deepak
I am using Devise as my authentication system and simple form. I get a NoMethodError in Groups#show and an undefined method 'name' for nil:NilClass error. I use model associations to tie the groups and posts together. When I do puts post.user.name it correctly displays in my terminal but that line causes the above error and it's referencing Groups#show for some reason. Any thoughts?
Routes
resources :groups do
resources :posts
end
Group Model
class Group < ActiveRecord::Base
validates :user_id, presence: true
belongs_to :user
has_many :posts
has_many :comments
has_many :attachments
end
Post Model
class Post < ActiveRecord::Base
validates :user_id, presence: true
validates :caption, presence: true
belongs_to :user
belongs_to :group
has_many :comments, dependent: :destroy
end
Group Controller
class GroupsController < ApplicationController
before_action :authenticate_user!
def new
#group = current_user.groups.build
end
def create
#group = current_user.groups.build(group_params)
#group.user_id = current_user.id
if #group.save
redirect_to groups_path
else
render :new
end
end
...
private
def group_params
params.require(:group).permit(:group_name, :description, :user_id)
end
end
Posts Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :owned_post, only: [:edit, :update, :destroy]
before_action :authenticate_user!
def index
#posts = Post.paginate(page: params[:page], per_page: 3).order('created_at DESC')
#post = current_user.posts.build
#attachments = Attachment.all
end
...
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
#group = Group.find(params[:group_id])
#post.group_id = #group.id
if #post.save
redirect_to groups_path
else
render :new
end
end
...
private
def post_params
params.require(:post).permit(:caption, :user_id)
end
def set_post
#post = Post.find(params[:id])
end
def owned_post
unless current_user == #post.user
redirect_to root_path
end
end
end
groups/show.html.erb
<%= render "posts/index" %>
...
posts/_index.html.erb
<%= render 'posts/form' %>
<%= render 'posts/posts' %>
...
posts/_form.html.erb
<%= simple_form_for([#group, #group.posts.build]) do |f| %>
...
posts/_posts.html.erb
<% #group.posts.each do |post| %>
<%= puts post.user.name %> ISSUE
<%#<%= render 'posts/post', post: post %>
<% end %>
SOLUTION: After asking on Reddit Rails, a generous user offered a solution that works. Apparently the <%= simple_form_for([#group, #group.posts.build]) do |f| %> creates a new post and adds it to the groups.posts array and so this causes issues when it iterates over _posts.html.erb and there is no user. More information can be found here: https://www.reddit.com/r/rails/comments/4lygix/unidentified_method_for_nil_class_devise_and/
But replace the above simple_form line of code with <%= simple_form_for([#group, Post.new(group: #group)]) do |f| %> seemed to do the trick, as suggested by the generous user in the reddit link above.
I have a model pitch where i am fetching grounddetail_id. I want to show all the pitch available in the ground. How i can book pitch of ground..
grounddetails_controller.rb
class GrounddetailsController < ApplicationController
before_action :find_ground, only: [:show, :edit, :destroy, :update]
def index
#grounddetails = Grounddetail.all.order('created_at DESC')
end
def new
#grounddetail = Grounddetail.new
end
def edit
end
def show
end
def create
#grounddetail = Grounddetail.new(ground_params)
if #grounddetail.save
redirect_to #grounddetail
else
render 'new'
end
end
def update
if #grounddetail.update(ground_params)
redirect_to #grounddetail
else
render 'edit'
end
end
def destroy
#grounddetail.destroy
redirect_to root_path
end
private
def find_ground
#grounddetail = Grounddetail.find(params[:id])
end
def ground_params
params.require(:grounddetail).permit(:name, :working_hours, :end_time, :address, :contact_no, :email, :number_of_grounds, :description, :featured_ground)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users
devise_for :admins
resources :grounddetails do
resources :pitches
end
root "grounddetails#index"
end
model
grounddetail.rb
class Grounddetail < ActiveRecord::Base
has_many :pitches, dependent: :destroy
end
pitch.rb
class Pitch < ActiveRecord::Base
belongs_to :grounddetail
end
for now i just have pitch model and routes but in controller i am confused what to use. i can i book pitch of the ground. But for single ground i am able to book.
In the grounddetails#show action
def show
#pitches = #grounddetail.pitches
end
Then in the groundetails/show.html.erb, you can just use <%= #pitches %> to display the pitches of that grounddetail.
Update:
#in the show.html.erb
<% #pitches.each do |p| %>
<%= form_tag book_pitch_path(p) do %>
#your attributes here
<%= submit_tag "Book this Pitch" %>
<% end %>
<% end %>
#routes.rb
post :book_pitch/:id, to: 'pitches/book_pitch', as: 'book_pitch'
#in pitches_controller.rb
def book_pitch
#your actions here
end
I am trying to set up a 5 star rating system so users can rate other users. At the moment everything is working, (create, delete, update etc...) but only the logged in user can rate himself. I cannot rate other users. I get no errors, it just redirects to the user profile page as it should but without added a rating to that user.
user.rb
class User < ActiveRecord::Base
has_many :reviews
review.rb
class Review < ActiveRecord::Base
belongs_to :user
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :find_user
before_action :find_review, only: [:edit, :update, :destroy]
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to user_path(#user)
else
render 'new'
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to user_path(#user)
else
render 'edit'
end
end
def destroy
#review.destroy
redirect_to user_path(#user)
end
private
def review_params
params.require(:review).permit(:rating, :comment)
end
def find_user
#user = User.find(params[:user_id])
end
def find_review
#review = Review.find(params[:id])
end
end
_form which then gets rendered on show page:
<%= simple_form_for([#user, #user.reviews.build]) do |f| %>
<div id="rating-form">
<label>Rating</label>
</div>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
<script>
$('#rating-form').raty({
path: '/assets/',
scoreName: 'review[rating]'
});
</script>
Any help getting this to work would be greatly appreciated!!
Do this:
#config/routes.rb
resources :users do
resources :reviews, only: [:new, :create]
end
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :reviewed, class_name: "User", foreign_key: :reviewed_id
end
#app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
def new
#review = current_user.reviews.new
end
def create
#review = current_user.reviews.new review_params
#review.save
end
private
def review_params
params.require(:review).permit(:rating, :comment).merge(reviewed_id: params[:user_id])
end
end
#app/views/reviews/new.html.erb
<%= form_for #review do |f| %>
<%= f.number_field :rating %>
<%= f.text_field :comment %>
<%= f.submit %>
<% end %>
This would mean you'll have to include a reviewed_id column in your reviews table.
You'll be able to access it using: url.com/users/:user_id/reviews/new
The application will automatically fill the user_id and reviewed_id fields, so the rest of your code should work with the upgrade.
The big problem you have is that you're basically recording the user_id (presumably of who created the review)... but have no way of stipulating who the review is about.
The above code fixes that for you.
I'm somewhat of a newbie with ruby on rails and went off of samurails.com single table inheritance with rails 4 tutorial to add different comment types. This worked great but the problem I'm running into is when I try to use polymorphic associations to get comments and the specific type to function under other models such as project and challenge. A regular comment works, but the specific types do not.
I haven't seen anything that clearly says how to make this work or another option of going about it so any help would be greatly appreciated.
class Comment < ActiveRecord::Base
has_merit
acts_as_votable
belongs_to :commentable, :polymorphic => true
belongs_to :user
belongs_to :commenttype
belongs_to :project
def self.types
%w(Question Idea Problem)
end
def commentable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
scope :questions, -> {where(type: 'Question')}
scope :ideas, -> {where(type: 'Idea')}
scope :problems, -> {where(type: 'Problem')}
end
class Question < Comment
end
class Idea < Comment
end
class Problem < Comment
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :comments, :as => :commentable, :class_name => "Comment"
has_many :questions, :as => :commentable, :class_name => "Question"
has_many :ideas, :as => :commentable, :class_name => "Idea"
has_many :problems, :as => :commentable, :class_name => "Problem"
delegate :questions, :ideas, :problems, to: :comments
end
class CommentsController < ApplicationController
before_action :set_commentable, only: [:index, :new, :create]
before_action :set_type
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def index
#comments = type_class.all
end
def show
end
def new
#comment = type_class.new
end
def edit
end
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_to :back, notice: "#{type} was successfully added."
else
render action: 'new'
end
end
def update
if #comment.update(comment_params)
redirect_to #comment.commentable, notice: "#{type} was successfully updated."
else
render action: 'edit'
end
end
def destroy
#user = current_user
#comment = #commentable.comments.where(comment_user: current_user).first
#commentable.comment.destroy
respond_to do |format|
format.html { redirect_to #commentable, notice: "Comment was deleted." }
format.js
end
end
private
def set_comment
#comment = type_class.find(params[:id])
end
def set_type
#type = type
end
def type
Comment.types.include?(params[:type]) ? params[:type] : "Comment"
end
def type_class
type.constantize
end
def set_commentable
#commentable = find_commentable
end
# add more commentable models here
def find_commentable
if params[:challenge_id]
Challenge.find(params[:challenge_id])
else
end
end
def find_commentable
if params[:project_id]
Project.find(params[:project_id])
else
end
end
def comment_params
params.require(type.underscore.to_sym).permit(:body, :type, :user_id, :commentable_id, :commentable_type, :commentable, :comment_type)
end
end
module CommentsHelper
def sti_comment_path(type = "comment", comment = nil, action = nil)
send "#{format_sti(action, type, comment)}_path", comment
end
def format_sti(action, type, comment)
action || comment ? "#{format_action(action)}#{type.underscore}" : "#{type.underscore.pluralize}"
end
def format_action(action)
action ? "#{action}_" : ""
end
end
<%= form_for [commentable, Comment.new], :html => { :multipart => true } do |f| %>
<%= f.text_area :body, class: "form-control", placeholder: "What's on your mind?" %>
<%= f.label :type %><br>
<%= f.select :type, Comment.types.map {|r| [r.humanize, r.camelcase]}, {}, disabled: #type != "Comment" %>
<%= f.submit "Post", class: "btn pull-right" %>