i'm getting this error for my products and user table.
--Couldn't find user without an id
def set_user
#user = User.find(params[:user_id])
end
I have nested the routes like so..
resources :users do
resources :products do
resources :reviews
end
end
and here is my products controller..
class ProductsController < ApplicationController
before_action :require_signin, except: [:index, :show]
before_action :set_user
def index
#products = #user.products
end
def show
#product = Product.find(params[:id])
end
def edit
#product = Product.find(params[:id])
end
def update
#product = Product.find(params[:id])
if #product.update(product_params)
redirect_to [#user, #product], notice: "Product successfully updated!"
else
render :edit
end
end
def new
#product = #user.products.new
end
def create
#product = #user.products.new(product_params)
#product.user = current_user
if #product.save
redirect_to user_products_path(#product, #user), notice: "Product successfully created!"
else
render :new
end
end
def destroy
#product = Product.find(params[:id])
#product.destroy
redirect_to user_products_path(#product, #user), alert: "Product successfully deleted!"
end
private
def product_params
params.require(:product).permit(:title, :description, :posted_on, :price, :location, :category)
end
def set_user
#user = User.find(params[:user_id])
end
end
All i am trying to do is associate the user and product so the product belongs_to user, and the user has_many products.
class Product < ActiveRecord::Base
belongs_to :user
has_many :reviews
class User < ActiveRecord::Base
has_secure_password
has_many :reviews, dependent: :destroy
has_many :products, dependent: :destroy
As other users have mentioned the params[:user_id] value is probably nil.
That said, you already appear to have a current_user defined in the scope of the controller. I see it referenced in the create action. I'd bet that it was set by the require_sign_in before_action. Given what I think you are trying to do, it probably makes your set_user before_action a bit redundant.
You can probably just refer to current_user in your controller anywhere you are currently using #user. Alternatively, you might set #user = current_user in the set_user before_action.
SideNote:
Looking a bit closer at your create action:
def create
#product = #user.products.new(product_params)
#product.user = current_user
if #product.save
redirect_to user_products_path(#product, #user), notice: "Product successfully created!"
else
render :new
end
end
Correct me if I'm wrong but I believe doing something like #model.association.new sets the model_id for the newly created association object so I would change the two lines
#product = #user.products.new(product_params)
#product.user = current_user
to simply be
#product = current_user.products.new(product_params)
For any action of your controller you should pass user_id param.
The reason of error is params[:user_id] equal nil
Related
I have some polymorphic relationships set up and are working well for the primary purpose. That is for a User to be able to Comment on both Articles and Coffeeshops.
However I'm struggling with being able to display the users list of comments on their profile page. In the future I also want the user to be able to 'favourite' and 'want to go to' different coffeeshops which I would also want to show up on their profile page. I'm hoping once I get the logic for display current comments, the rest will be a breeze ;)
So what I have:
Models
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
Comment Controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :authenticate_user!
before_action :comment_auth, only: [:edit, :update, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(allowed_params)
#comment.user_id=current_user.id if current_user
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #commentable
else
render 'edit'
end
end
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
flash[:success] = "Comment Destroyed!"
redirect_to :back
end
end
private
def allowed_params
params.require(:comment).permit(:name, :body)
end
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:body).merge(user_id: current_user.id)
end
Profile Controller
class ProfileController < ApplicationController
before_action :authenticate_user!
def index
end
def show
#user = User.find.current_user(params[:id])
#comments = #commentable.comments
end
In views/profile/show.html.erb. I was trying to do:
<h3>Your Latest Comment</h3>
<%=#comment.user.body%>
But this clearly isn't right as I get Couldn't find User without an ID. From ProfileController#show
update
If I change ProfileController to
before_action :authenticate_user!
def index
#user = User.find.current_user(params[:user_id])
end
def show
#comments = #commentable.comments
end
I get an error for undefined comments.
ok first return this to show moving it to index is not solving a problem the index is not called so write show like this.
def show
#user = current_user #you get instance of a user that is logged in
#comments = #user.comments
end
I do not know if you have user_id in your comment migration but if you do not have you must write
class User < ApplicationRecord
has_many :comments, as: :commentable
end
view
<h3>Your Latest Comment</h3>
<%=#comments.try(&:last).try(&:body)%>
i'm following the tutorial from Michael Hartl and created a shopping cart which i encountered few issues with.
each user can create a new shopping cart with different 'id', but when different user add product to cart, the added products adds in all carts of different 'id' instead of that particular cart by current_user
how to restrict user to only view their own cart, without able to view other user cart?
please guide to resolve issues above, much appreciated with thanks!
user.rb (not a complete code because it will be lengthy, added the 'has_one :cart' besides original codes from Michael Hartl tutorial)
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
has_many :orders
has_one :cart
cart.rb
class Cart < ActiveRecord::Base
has_many :line_items, dependent: :destroy
belongs_to :user
def add_product(product_id)
current_item = line_items.find_by(product_id: product_id)
if current_item
current_item.quantity += 1 #quantity of line_item, product in cart
else
current_item = line_items.build(product_id: product_id)
end
current_item
end
def total_price
line_items.to_a.sum { |item| item.total_price }
end
end
concerns/Current_Cart.rb
module CurrentCart
extend ActiveSupport::Concern
private
def set_cart
#cart = current_user.cart || current_user.create_cart
session[:cart_id] = #cart.id
end
end
line_items_controller.rb
class LineItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create] #before create, execute :set_cart, find(or create) cart
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
def index
#line_items = LineItem.all
end
def show
end
def new
#line_item = LineItem.new
end
def edit
end
def create
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
if #line_item.save
redirect_to current_user.cart
else
render :new
end
end
def update
if #line_item.update(line_item_params)
redirect_to #line_item, notice: 'Line item was successfully updated.'
else
render :edit
end
end
def destroy
#line_item.destroy
redirect_to line_items_url, notice: 'Line item was successfully destroyed.'
end
private
def set_line_item
#line_item = LineItem.find(params[:id])
end
def line_item_params
params.require(:line_item).permit(:product_id)
end
end
carts_controller.rb
class CartsController < ApplicationController
before_action :set_cart, only: [:edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
def show
#cart = current_user.cart
end
def edit
end
def update
if #cart.update(cart_params)
redirect_to #cart, notice: 'Cart was successfully updated.'
else
render :edit
end
end
def destroy
#cart.destroy if #cart.id == session[:cart_id]
session[:cart_id] = nil
redirect_to store_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cart
#cart = Cart.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cart_params
params.fetch(:cart, {})
end
def invalid_cart
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
end
end
if im logged in as an user with id '1', i created my cart with id '1'. I logged out, sign in again with another account with id '2', created a cart with id '2', but when i access another cart with link cart/1, im still able to see the cart from another user which not suppose to happen. Hope u understand –
The reason you can view another individual's cart is due to the controller code.
Whenever you show a cart, first the controller sets the cart using set_cart from within the controller.
def set_cart
#cart = Cart.find(params[:id])
end
This will fetch whatever cart with a specific ID.
Then show will display any cart that is passed to it.
def show
#cart = current_user.cart
end
What you should be doing is using current_cart.rb to set the cart and remove the existing set_cart from the controller. Also, make set_cart in current_cart.rb public.
You will also need to change your show route, since it is expecting an :id, and now we're not telling the server which cart to view.
I forget exactly where the book includes CurrentCart, I believe it was in ApplicationController. If so, then before_action :set_cart, only[...] should work just fine with other logic.
I am trying to implement nested answers into comments, which are nested into auctions.
There is a auctions.rb model, which:
has_many :comments, dependent: :destroy
has_many :answers, :through => :comments
a comments.rb model, which:
belongs_to :auction
has_many :answers, dependent: :destroy
a answers.rb model, which:
belongs_to :comment
the answers_controller inherits from the comments_controller:
class AnswersController < CommentsController
before_action :all_answers, only: [:index, :create, :update, :destroy]
before_action :set_answer, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Answer (Form)
def new
#answer = Answer.new
#comments.answers.build
end
# Create Answer
def create
#answer = #comment.answers.build(answer_params)
#answer.user_id = current_user.id
#answer.save
end
# Edit Answer
def update
#answer.update!(answer_params)
end
# Delete Answer
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
private
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
def answer_params
params.require(:comment).permit(:body)
end
end
The Error:
NoMethodError in Auctions#show app/views/comments/_comment.html.erb
where line #20 raised: undefined method `answers' for nil:NilClass
<div class="col s12" id="answer-form" style="display:none;"></div>
</div>
<div class="row">
<div class="col s12" id="answers"><%= render #comment.answers %></div>
</div>
With <%= render #comment.answers %> I want to display all existing answers below the related comment. What am I doing wrong?
auction_controller
class AuctionsController < ApplicationController
# Index of all auctions
def index
#auctions = Auction.all
end
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
# New Auction Form
def new
#auction = Auction.new
end
# Edit Auction
def edit
#auction = Auction.find(params[:id])
end
# Create new Auction
def create
# Create new Auction
#auction = Auction.new(auction_params)
# Save Id of User (Seller)
#auction.user_id = current_user.id
# If auction was created successfully
if #auction.save
# display the created auction
redirect_to #auction, :notice => "Auction created"
else
# display Form again if unsuccessful
render 'new'
end
end
# Update existing Auction
def update
#auction = Auction.find(params[:id])
# Validation
if #auction.update(auction_params)
redirect_to #auction, :notice => "Auction updated"
else
render 'edit'
end
end
# Delete Auction
def destroy
#auction = Auction.find(params[:id])
#auction.destroy
redirect_to auctions_path, :notice => "Auction deleted"
end
private
# set required parameters for new created Auctions
def auction_params
params.require(:auction).permit(:condition, :product_name)
end
end
comments_controller
class CommentsController < ApplicationController
before_action :set_auction
before_action :all_comments, only: [:index, :create, :update, :destroy]
before_action :set_comment, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Comment (Form)
def new
#comment = Comment.new
#auction.comments.build
end
# Create Comment
def create
#comment = #auction.comments.build(comment_params)
#comment.user_id = current_user.id
#comment.save
end
# Edit Comment
def update
#comment.update!(comment_params)
end
# Delete Comment
def destroy
#comment = Comment.find(params[:id])
#auction = #comment.auction
#comment.destroy
end
private
def set_auction
#auction = Auction.find(params[:auction_id])
end
def all_comments
#comments = #auction.comments.all
end
def set_comment
#comment = #auction.comments.find(params[:id])
end
def comment_params
params.require(:comment).permit(:body)
end
end
Normal Comments work. Only Comment Answers don't work.
The error happens in Auctions#show, the error clearly tells you that you are trying to call answers on a nil object. Therefore, it means #comment is nil in that view.
In fact, if you check the show action, you never fetch/assign any object to #comment.
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
In order to fix it, make sure #comment is properly assigned to a Comment instance.
There's another problem here:
def new
#answer = Answer.new
#comments.answers.build
end
You haven't got a variable called #comments, hence your form can't actually build answers off it. In fact, you're calling #comment in two other methods, where I can't even see it being declared:
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
The only time you've declared #commentis in the destroymethod:
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
Even then, it's weird.
Why are you calling the Comment model with the #answer variable? Surely you'd have an Answer model with comments attached by way of a has_many relationship?
I'd recommend you keeping it brain-dead simple:
#app/models/answer.rb
class Answer < ActiveRecord::Base
has_many :comments
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :answer
end
This means when you call your actions controller, you'll be able to apply the following:
def show
#answer = Answer.find params[:id]
#comment = #answer.comments.build
end
If you wanted to have comments act as answers, you need to keep the models separate. Use a hierchy gem, like closure tree. This way, you'll be able to keep your Answers/Comments in hierarchy order, whilst keeping the Models consistent.
I was following following this online video (here) tutorial for a simple blog.
Inside these blog posts are comments.
In this tutorial the Devise gem was used and a migration was performed to assign add user_id to posts.
I did a migration of add_user_id_to_comments similar to was done in the user_id migration for posts.
class AddUserIdToComments < ActiveRecord::Migration
def change
add_column :comments, :user_id, :integer
add_index :comments, :user_id
end
end
My models contain the following:
Comment model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
User model
class User < ActiveRecord::Base
has_many :posts
has_many :comments
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I can assign a comment to a user doing the following.
user = User.first
comment = Comment.first
comment.user = user
This works in the console but the problem I'm running into is the comments_controller.
I've tried to mimic the steps done with the post controller.
This is from the Post controller (which works fine)
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :post_auth, only: [:edit, :update, :destroy]
def index
#posts = Post.all.order('created_at DESC')
end
def new
##post = Post.new
#post = current_user.posts.build
end
def create
##post = Post.new(post_params)
#post = current_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
end
def edit
end
def update
if #post.update(params[:post].permit(:title, :body))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def find_post
#post = Post.find(params[:id])
end
# Checks if current user authored pin
def post_auth
if current_user != #post.user
redirect_to(root_path)
end
end
end
The Comments controller I'm having difficulty with
class CommentsController < ApplicationController
before_action :find_comment, only: [:create, :destroy]
before_action :comment_auth, only: [:destroy]
def create
##post = Post.find(params[:post_id])
##comment = #post.comments.create(params[:comment].permit(:name, :body))
#redirect_to post_path(#post)
#new format
#post = Post.find(params[:post_id])
#post = current_user.posts.build(post_params)
#comment = current_user.comments.build(comment_params)
redirect_to post_path(#post)
end
def destroy
#comment = #post.comments.find(params[:id]).destroy
redirect_to post_path(#post)
end
private
def comment_params
params.require(:comment).permit(:name, :body)
end
def find_comment
#post = Post.find(params[:post_id])
end
def comment_auth
if current_user != #post.user
redirect_to(root_path)
end
end
end
When I attempt to create a new comment; nothing happens...it redirects to the post page.
There are no errors returned however.
Your thoughts how to resolve this issue?
Any help will be appreciated.
EDIT w/ SOLUTION
I was running into issues where the the 'name and body values were not being passes to the database. After several attempts I finally was able to the name and body values passed with the following code:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:name, :body))
#comment.user_id=current_user.id if current_user
#comment.save
if #comment.save
redirect_to post_path(#post)
else
render 'new'
end
Thank you all for the assistance
#comment = current_user.comments.build(comment_params)
You're calling build on your comments and then not saving them. You either need to save or use create.
When you use the build method, an object is returned but it hasn't yet been saved to the database. You have two options:
Manually save the object:
#post = Post.find(params[:post_id])
#comment = current_user.comments.build(comment_params)
#comment.save!
redirect_to post_path(#post)
or,
Use the create method, which automatically saves (note that I haven't tested this, feedback welcome if I'm telling lies!):
#post = Post.find(params[:post_id])
#comment = current_user.comments.create(comment_params)
redirect_to post_path(#post)
See the differences between the two in the Rails docs (emphasis mine):
collection.build(attributes = {}, …)
Returns one or more new objects of the collection type that have been instantiated with attributes and linked to this object through a foreign key, but have not yet been saved.
collection.create(attributes = {})
Returns a new object of the collection type that has been instantiated with attributes, linked to this object through a foreign key, and that has already been saved (if it passed the validation). Note: This only works if the base model already exists in the DB, not if it is a new (unsaved) record!
class CommentsController < ApplicationController
# Omitted some code code
def create
##post = Post.find(params[:post_id])
##comment = #post.comments.create(params[:comment].permit(:name, :body))
#redirect_to post_path(#post)
#new format
#post = Post.find(params[:post_id])
#post = current_user.posts.build(post_params)
#comment = current_user.comments.build(comment_params)
redirect_to post_path(#post)
end
# Omitted more code
end
In the create action, you did not save the #comment, rather you are only building it. You should call #comment.save or current_user.comments.create(comment_params). To illustrate what I am saying:
Either this:
def create
#post = Post.find(params[:post_id])
#comment = current_user.comments.create(comment_params)
redirect_to post_path(#post)
end
or this, will work.
def create
#post = Post.find(params[:post_id])
#comment = current_user.comments.build(comment_params)
#comment.save # returns true if save successful, false otherwise
redirect_to post_path(#post)
end
I am a learner and even I have created same app and have found the way , just check if it helps. Solution is to add merge() to params
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body).merge(user_id: current_user.id)
end
I am new in rails and I am getting ActiveModel::ForbiddenAttributesError. This is my controller for nested resource:
class PostsController < ApplicationController
respond_to :html, :xml, :json
def index
#post=Post.all
end
def new
#user = User.find(params[:user_id])
#post = #user.posts.build
respond_with(#post)
end
def create
debugger
#user = User.find(params[:user_id])
#post = #user.posts.build(params[:post])
if #post.save
redirect_to user_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title, :description, {:user_ids =>[]})
end
end
If you have the following models:
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
has_many :posts
end
You don't actually need to use nested attributes, just do the following in your controller:
class PostsController < ApplicationController
respond_to :html, :xml, :json
def index
#post = Post.all
end
def new
#post = Post.new
respond_with(#post)
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.new(post_params) # This will automatically set user_id in the post object
if #post.save
redirect_to user_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title, :description)
end
end
Although I don't recommend that you user a user_id param in the url. Have a look at the devise gem to handle authentication.