I am new to rails and i keep getting this error
Couldn't find User without an ID
from:
class UserController < ApplicationController
def show
#user = User.find(params[:id])
end
end
this is what i have;
model/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
validates :name, presence: true
validates :email, presence: true
has_many :listings, dependent: :destroy
has_many :purchasing, class_name: "Transaction", foreign_key: "buyer_id", dependent: :destroy
has_many :sell, class_name: "Transaction", foreign_key: "seller_id", dependent: :destroy
has_many :purchased, class_name: "Archive", foreign_key: "buyer_id", dependent: :destroy
has_many :sales, class_name: "Archive", foreign_key: "seller_id", dependent: :destroy
has_many :selling_rooms, class_name: "Room", foreign_key: "seller_id", dependent: :destroy
has_many :buying_room, class_name: "Room", foreign_key: "buyer_id", dependent: :destroy
def can_buy?(listing_price)
if self.points >= listing_price
true
else
false
end
end
def withdraw(listing_price)
self.points -= listing_price
end
def purchasing_list
purchasing.includes(:seller, :listing)
end
def purchased_list
purchased.includes(:seller, :listing)
end
def sell_list
sell.includes(:seller, :listing)
end
def sales_list
sales.includes(:seller, :listing)
end
end
resources
resources :users
I looked around but all i could find was something saying that it is looking for a resource that doesn't exist.
It seems that params[:id] not present.
first try to check with putting in
class UserController < ApplicationController
def show
logger"-----#{params[:id]}---"
#user = User.find(params[:id])
end
end
if its empty then pass id from the link from where you getting this error
ex. localhost:3000/users/1
here params[:id] == 1 must be
Some where in your form which redirects you to the users show page you should send the id or send the object itself.
<%= link_to 'Show', user_path(user) %>
or
<%= link_to 'Show', user_path(user.id) %>
Please check your params, As I know you are getting params[:id] = nil.
Try to pass :id in your params or you can check it by hard-coding it.
Check yo console!
In the parameters that are sent through in the request that is blowing up, have a look at exactly what the id your sending through is. Is it a user_id? is it an id? Your controller is expecting an id so make sure you are passing it one.
If you're passing an entire object you will need to specify. Eg. If you're passing through a user you'll need to pass through a user.id instead because your controller is expecting an ID which is typically a number. user will give the whole object, user.id will give just the number value, which is the ID in this case. Then with that number the controller action can find the relevant user in the database.
Related
My goal is to display in the notifications modal that a follower has started following the current user whenever the user clicks the follow button.
I am also using the noticed gem, but it seems a bit complicated for me to implement with my relationship model (Which is the follower/following model).
Whenever I follow someone, I see in the console that it is inserting the notification, but when I click unfollow I get an error that there are "too many has_many associations". And when I log in as the user that gets followed the notification does not appear. I am assuming because I have implemented the notify recipient function wrong.And I cannot seem to find any resources only for follow notifications.
Here is my code:
FollowNotification.rb
def message
#user = User.find(follower_id: params[:user_id])
"#{#user.username} has started following you"
end
#
def url
show_user_path(#user)
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!
def create
#user = User.find(params[:user_id])
# if statement prevents user from forcing both users to follow each other after accepting request
if current_user.Is_private? && !#user.pending_requests
following = #user.relationships.build(follower_id: current_user.id)
following.save
redirect_to request.referrer || root_path
else
following = current_user.relationships.build(follower_id: params[:user_id])
following.save
redirect_to request.referrer || root_path
end
end
def destroy
following = current_user.relationships.find_by(follower_id: params[:user_id])
following.destroy
redirect_to request.referrer || root_path
end
end
Relationship model
class Relationship < ApplicationRecord
belongs_to :following, class_name: 'User'
belongs_to :follower, class_name: 'User'
has_noticed_notifications model_name: 'Notification'
has_many :notifications, through: :user, dependent: :destroy
after_create_commit :notify_recipient
before_destroy :cleanup_notifications
private
def notify_recipient
FollowNotification.with(follower: self).deliver_later(following.id)
end
def cleanup_notifications
notifications_as_follow.destroy_all
end
end
User model
class User < ApplicationRecord
has_merit
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar
validates :avatar, file_size: { less_than_or_equal_to: 5.megabytes },
file_content_type: { allow: ['image/jpg', 'image/png', 'image/jpeg'] }
has_many(
:posts,
class_name: 'Post',
foreign_key: 'user_id',
inverse_of: :user
)
has_many :likes
has_many :comments
validates :username, presence: true, length: {maximum: 30}
validates_uniqueness_of :username
has_many :relationships, foreign_key: :following_id
has_many :followings, through: :relationships, source: :follower
has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id
has_many :followers, through: :reverse_of_relationships, source: :following
def is_followed?(user)
reverse_of_relationships.find_by(following_id: user.id).present?
end
has_many :notifications, as: :recipient, dependent: :destroy
end
I have nested relationships and built them according to the Rails Guide.
A User has many Collections that have many Sections each containing many Links. When creating a new Link though, the user_id is not being assigned but is always nil. The section_id and collection_id are being set correctly.
Controller
class Api::V1::LinksController < Api::V1::BaseController
acts_as_token_authentication_handler_for User, only: [:create]
def create
#link = Link.new(link_params)
#link.user_id = current_user
authorize #link
if #link.save
render :show, status: :created
else
render_error
end
end
private
def link_params
params.require(:resource).permit(:title, :description, :category, :image, :type, :url, :collection_id, :user_id, :section_id)
end
def render_error
render json: { errors: #resource.errors.full_messages },
status: :unprocessable_entity
end
end
Models
User
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
acts_as_token_authenticatable
has_many :collections, dependent: :destroy
has_many :sections, through: :collections, dependent: :destroy
has_many :links, through: :sections, dependent: :destroy
mount_uploader :image, PhotoUploader
end
Collection
class Collection < ApplicationRecord
belongs_to :user
has_many :sections, dependent: :destroy
has_many :links, through: :sections, dependent: :destroy
mount_uploader :image, PhotoUploader
end
Section
class Section < ApplicationRecord
belongs_to :collection
has_many :links, dependent: :destroy
end
Link
class Link < ApplicationRecord
belongs_to :section
end
Is this the correct way to set up the relationships and can someone help me understand what I am missing?
You can't do
#link.user_id = current_user
You could (instead) do...
#link.user_id = current_user.id
Or more elegantly...
#link.user = current_user
Which assumes you will define the relationship in the model
class Link < ApplicationRecord
belongs_to :section
belongs_to :user
end
But as Andrew Schwartz points out in the comments, it may have been a design mistake to add the field user_id to the links table. You have in the User model has_many :links, through: :sections, dependent: :destroy which does not use any user_id field in the link record. It uses the user_id field in the collections table
Just adding user_id to the links table will NOT mean that link will be returned when you do my_user.links ... it won't be.
Since you're passing a section_id in the link_params that is enough to create the link to the user, so just write a migration to remove the user_id field. If you want to be able to see the associated user from the link, do...
class Link < ApplicationRecord
belongs_to :section
has_one :collection, through: :section
has_one :user, through: :collection
end
and that will let you do my_link.user to retrieve the link's user.
I have setup a polymorphic liking in my my app where a user can like other models e.g book, chapter, article... Now I tried to take it a little further by allowing a user like another user but I'm running to this error:
Validation failed: User must exist
pointing to
likes.where(item: item).create!
This is my initial setup for liking other models excluding the user model
like.rb
belongs_to :user
belongs_to :item, polymorphic: true
user.rb
has_many :likes, dependent: :destroy
def toggle_like!(item)
if like = likes.where(item: item).first
like.destroy
else
likes.where(item: item).create!
end
end
def likes?(item)
likes.where(item: item).exists?
end
likes_controller.rb
class LikesController < ApplicationController
before_action :authenticate_user!
def toggle
if params[:book_id]
item = Book.friendly.find(params[:book_id])
elsif params[:user_id]
item = User.find_by(username: params[:username])
end
current_user.toggle_like!(item)
redirect_back(fallback_location: root_path)
end
end
book.rb
has_many :likes, as: :item, dependent: :destroy
For a user to like another user, i adjusted the user.rb from
has_many :likes
to
has_many :likes, as: :item, dependent: :destroy
This is when I get the error
Validation failed: User must exist
pointing to
likes.where(item: item).create!
in the user.rb
Keep has_many :likes, dependent: :destroy and also add has_many :received_likes, as: :item, class_name: "Like", dependent: :destroy. I think that will fix it. Because when you put User has_many: :likes, as: :item, and removed User has_many: :likes, this means that the association Like belongs_to :user was one-sided.
first of all sorry for English
So i already have "user - posts" one to many association, which means that each post can have just ONE author, and now i want to add "favorite posts" button to user profile, and "add to favorite" button to each post, so the question is how to implement this correct way? should i rework my user - post association?
or create some another model? I,m a bit confused. Thank in advance !
Actually i want this result :
#user.posts #return all posts created by this user
#user.favorite_posts #return posts added to favorites by this user
Here is my User model:
class User < ApplicationRecord
mount_uploader :avatar, ImageUploader
validates :username, presence: true, uniqueness: true, length: {in: 3..20}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :ratings
enum role: [ :user, :admin ]
def calculate_average
ratings.blank? ? 0 : ratings.map(&:value).inject(:+) / ratings.count.to_f
end
end
Post model:
class Post < ApplicationRecord
mount_uploader :image, ImageUploader
validates :body, presence: true
validates :title, presence: true, length: { maximum: 50}
belongs_to :user
has_many :comments, dependent: :destroy
end
EDIT
Alright look how i've done this, it works exactly the way I wanted it.
Here is my user model:
class User < ApplicationRecord
mount_uploader :avatar, ImageUploader
validates :username, presence: true, uniqueness: true, length: {in: 3..20}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :ratings
has_many :favorites, dependent: :destroy
has_many :favorite_posts, through: :favorites, source: "post"
enum role: [ :user, :admin ]
def calculate_average
ratings.blank? ? 0 : ratings.map(&:value).inject(:+) / ratings.count.to_f
end
end
You need many-to-many relationships for the favorite post, at first run this command to create a table favorite_posts
rails g model FavoritePost user:references post:references
Then
rails db:migrate
Then add these to your model would look like this:
#=> model/user.rb
class User < ApplicationRecord
has_many :favorite_posts, dependent: :destroy # or you can use only this line except second if you will face any problem
has_many :posts, through: :favorite_posts
end
#=> model/post.rb
class Post < ApplicationRecord
has_many :favorite_posts, dependent: :destroy
has_many :users, through: :favorite_posts
end
#=> model/favorite_post.rb
class FavoritePost < ApplicationRecord
belongs_to :user
belongs_to :post
end
That was relation part, now create a favorite post part. For the fresh code you can create a controller, i.e.
rails g controller favorites
Then your routes file:
resources :favorites
An example of the new routes using rake routes:
favorites GET /favorites(.:format) favorites#index
POST /favorites(.:format) favorites#create
new_favorite GET /favorites/new(.:format) favorites#new
edit_favorite GET /favorites/:id/edit(.:format) favorites#edit
favorite GET /favorites/:id(.:format) favorites#show
PATCH /favorites/:id(.:format) favorites#update
PUT /favorites/:id(.:format) favorites#update
DELETE /favorites/:id(.:format) favorites#destroy
In your view file add something like this:
# For creating favorite
<%= link_to "Favorite", favorites_path(user: current_user, post: post.id), class: 'btn bf-save-btn', method: :post, data: {disable_with: "Saving..."}, title: "Add to favorite" %>
# For deleting favorite list
<%= link_to "Unfavorite", favorite_path(post.id), class: 'btn af-save-btn', method: :delete, data: {disable_with: "Removing...."}, title: "Remove from favorite" %>
In favorites_controller.rb:
def index
#saves = current_user.favorite_post
end
# index.html.erb
<% #saves.each do |fav| %>
<%= link_to fav.post.post_title, post_path(fav.post) %>
<% end %>
def create
#save = FavoritePost.new(post_id: params[:post], user: current_user)
respond_to do |format|
if #save.save
flash[:success] = 'Saved'
format.html { redirect_to request.referer }
format.xml { render :xml => #save, :status => :created, :location => #save }
else
format.html { redirect_to request.referer }
format.xml { render :xml => #save.errors, :status => :unprocessable_entity }
end
end
end
def destroy
post = Post.find(params[:id])
#save = FavoritePost.where(user_id: current_user.id, post_id: post.id).first
respond_to do |format|
if #save.destroy
flash[:error] = 'Unsaved'
format.html { redirect_to request.referer, status: 303 }
format.js { redirect_to request.referer, status: 303 }
# format.xml { head :ok }
end
end
end
That's it for favorite / unfavorite functionality. Now you need to create some logic for when to show Favorite and when Unfavorite.
For this requirements has many ways, at first you need to understand this then you can whatever you want.
Also, to achieve this without reloading your page you can try some Ajax.
Update
class User < ApplicationRecord
mount_uploader :avatar, ImageUploader
validates :username, presence: true, uniqueness: true, length: {in: 3..20}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :ratings
# Newly added
has_many :favorite_posts, dependent: :destroy # or you can use only this line except second if you will face any problem
has_many :posts, through: :favorite_posts
enum role: [ :user, :admin ]
def calculate_average
ratings.blank? ? 0 : ratings.map(&:value).inject(:+) / ratings.count.to_f
end
end
Hope it will help.
Create a new model for 'UserFavoritePost' stored post_id and user_id. And create a custom association for favorite_posts
class UserFavoritePost < ApplicationRecord
belongs_to :post
belongs_to :user
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :user_favorite_posts
has_many :favorite_posts, throught: :user_favorite_posts, class: 'Post'
end
fool-dev's answer does not provide a direct access to favorite posts, so the index view requires a loop. Prasanna's approach solves this, but his answer has been unfairly accused of incomplete and plagiarism :-). So here is the complete approach:
You need a many to many relationship, that´s true, so you need a join model and table. But this model is auxiliary. No important logic should be there, and I don´t think it deserves a controller or views.
class User < ApplicationRecord
has_many :posts, dependent: :destroy # Posts created by this user
has_many :favs, dependent: :destroy
has_many :fav_posts, through: :favs # Favorite posts for this user
end
class Post < ApplicationRecord
belongs_to :user
has_many :favs, dependent: :destroy
has_many :fav_users, through: :favs # Users who have this post as favorite
end
class Fav < ApplicationRecord
belongs_to :user
belongs_to :post
end
This allows to access all posts created by the user and all his favorite posts using two different methods in the user class.
#posts = current_user.posts # Posts created by this user
#fav_posts = current_user.fav_posts # Favorite posts
In the view:
<h1><% current_user.name %></h1>
<h2>Your posts</h2>
<%= render #posts %>
<h2>Your favorite posts from other users</h2>
<%= render #fav_posts %>
You don't need a controller to create, view or delete favorite posts. Just handle this logic in the User or Post controllers. For example, to favorite or unfavorite a post just add fav and unfav methods in the PostsController.
def fav
current_user.fav_posts << Post.find(params[:id])
end
def unfav
current_user.favs_posts.destroy(Post.find(params[:id]))
end
In the view:
<%= link_to "Favorite", fav_post_path(id: post.id) %>
<%= link_to "Unfavorite", unfav_post_path(id: post.id) %>
You should add these methods in your routes:
post '/posts/:id/fav', to: 'posts#fav', as: 'fav_post'
post '/posts/:id/unfav', to: 'posts#unfav', as: 'unfav_post'
So, this is pretty simple (I think) but I still have a hard time wrapping my head around this. I have an app, where as a User that is signed in can create Topics and Bookmarks which are nested in the topics. A User can also Like a bookmark another user created.
On my users/show.html.erb I'm trying to make a list of all the bookmarks and likes a user has created. Both have user_id's which I would assume that's how I could call them, I'm just retarded and need some help.
What would I put in my user_controller and my user/show.html.erb files?
Thanks for the help!
Here is my User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :topics
has_many :bookmarks, dependent: :destroy
has_many :likes, dependent: :destroy
#liked method takes a bookmark object and returns a like object if one exists
def liked(bookmark)
likes.where(bookmark_id: bookmark.id).first
end
end
Like.rb
class Like < ActiveRecord::Base
#considered a simple join table, that represents a relation between two objects (user/bookmark)
belongs_to :user
belongs_to :bookmark
end
Bookmark.rb
class Bookmark < ActiveRecord::Base
belongs_to :topic
belongs_to :user
has_many :likes, dependent: :destroy
end
In your user_controller:
def show
#user = User.find(user_params[:id])
end
def user_params
params.require(:user).permit(:id)
end
In your show.html.erb:
<% #user.bookmarks.each do |bookmark| %>
<%= bookmark.name %> #guessing here as I don't know your DB schema
<% end %>
<% #user.likes.each do |like| %>
<%= like.name %> #still guessing
<% end %>
You said Bookmarks are nested in Topics, but you don't show that in your code. Instead of User model having:
has_many :bookmarks, dependent: :destroy
it should be
has_many :bookmarks, through: :topics, dependent: :destroy
You can still access bookmarks the same way:
#user.bookmarks