Hey there,
I am fairly new to Rails and I've managed to create a Favorite controller for my Items(Tools) and Users, which works totally fine.Now I am trying to do the same just in that user module. So users are able to favorite other users. I played around, and came up with this:
I am getting this error in the browser when accessing /users/index view:
NoMethodError in Users#index
undefined method `favorite_user_path' for #<#<Class:0x8ca77b8>:0x8ca50b8>
Here is my code:
app/models/favorite_user.rb
class FavoriteUser < ActiveRecord::Base
belongs_to :c_user_id
belongs_to :user_id
end
app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :tools
# Favorite tools of user
has_many :favorite_tools # just the 'relationships'
has_many :favorites, through: :favorite_tools, source: :tool # the actual tools the user favorites
# Favorite users of user
has_many :favorite_users # just the 'relationships'
has_many :userfavorites, through: :favorite_users, source: :user # the actual users the user favorites
has_many :userfavorited_by, through: :favourite_users, source: :user # the actual users favoriting a user
mount_uploader :avatar_filename, AvatarUploader
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :find_user, only: [:show, :favorite]
# Add and remove favorite recipes
# for current_user
def userfavorite
type = params[:type]
if type == "favorite"
current_user.userfavorites << #user
elsif type == "unfavorite"
current_user.userfavorites.delete(#user)
else
# Type missing, nothing happens
redirect_to :back, notice: 'Nothing happened.'
end
end
def index
#users = User.all
end
def show
#tools = Tool.where(user_id: #user).order("created_at DESC")
#tool = Tool.find(1)
end
private
def find_user
#user = User.find(params[:id])
end
end
app/views/users/index.html.haml
- #users.each do |user|
= image_tag gravatar_for user if user.use_gravatar == true
= image_tag user.avatar_filename.url if user.use_gravatar == false
%h2= link_to user.username, user
%p= link_to "Favorite", favorite_user_path(user, type: "favorite"), method: :get
%p= link_to "Unfavorite", favorite_user_path(user, type: "unfavorite"), method: :get
app/config/routes.rb
resources :users, only: [:index, :show, :userfavorite] do
get :userfavorite, on: :member
end
I hope the provided data is enough, if not please tell me. I'm grateful for all Your replies.
You haven't declared any such path in the routes file. As per your routes file you can use named path like
userfavorite_user_path(user_id)
Related
I have an application with users using devise for authentication, in the user model I have added in the database a column called admin with false value by default. that way I have managed to have access as administrator to certain parts of the application.
I have a subscription model and each user when authenticated gets a free value by default. what I want to achieve is that the admin user in your user list can be able to switch from free to premium. this is the code i have and i can't get it to work.
Users Model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
#Validaciones
validates :nombre, :apellido, presence: true
devise :database_authenticatable, :validatable, password_length: 8..128
#Relaciones
has_many :patients, dependent: :destroy
has_many :articles, dependent: :destroy
has_one :profile, dependent: :destroy
has_one :suscription, dependent: :destroy
#Creación de perfil
after_create :set_profile
def set_profile
self.profile = Profile.create()
end
#Creación de suscripcion
after_create :set_suscription
def set_suscription
self.suscription = Suscription.create()
end
end
Suscription Model:
class Suscription < ApplicationRecord
belongs_to :user
enum status: {
free: 0,
premium: 1
}
end
Users controllers:
class UsersController < ApplicationController
def index
#pagy, #users = pagy(User.order(created_at: :asc), items:12)
end
def show
#user = User.find(params[:id])
end
end
Suscriptios controller:
class SuscriptionsController < ApplicationController
before_action :set_suscription
def show
end
def edit
end
def update
#suscription = Suscription.find(params[:id]).update_params
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
private
def set_suscription
#suscription = (current_user.suscription ||= Suscription.create)
end
def suscription_params
params.require(:suscription).permit(:status)
end
end
Route:
#UPDATE PREMIUM
patch "suscriptions", to:"suscriptions#update", as: "user_premium"
View (Link):
<%= link_to 'Update', user_premium_path ,method: :patch %>
This should fix it:
subscriptions_controller.rb
def update
#suscription = Suscription.find(params[:id]).update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
view
<%= link_to 'Update', user_premium_path(id: #subscription.id, status: "premium"), method: :patch %>
One other thing that is not needed, but normally I would see something like this in a controller:
private
def set_suscription
#suscription = Suscription.find(params[:id])
end
which then makes your update method look like this:
def update
#subscription.update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
This is all assuming you are simply trying to update the subscription from free to premium with your link_to. I wouldn't recommend doing anything like this, because what if someone accidentally marks this? They can no longer go back to a free subscription. Maybe have a modal open that is routed to subscription edit with a drop down to select the status would be better?
I have created a destroy action for my user profile picture. The destroy action works properly and the photo is deleted. I can add another picture without any problem, but when I try to delete the picture again I receive the following error message:
Couldn't find Avatar with 'id'=1
This is what is stored in my database Avatar.all reads:
[#<Avatar id: 2, avatarpic: "cat_astronaut.jpg", user_id: 1, created_at: "2017-12-05 17:02:08", updated_at: "2017-12-05 17:02:08">]
The id is at 2 and not 1, even though I successfully deleted the first picture with an id of 1. Every time I delete a picture and upload a new one, the id is incremented. So when I try to delete the second uploaded picture, it can't find the right id. I need the id to be reset to 1 every time I delete the picture.
Controller is as follows:
class AvatarsController < ApplicationController
def create
#user = User.find(params[:user_id])
#avatar = Avatar.new(avatar_params)
#avatar.user = #user
#avatar.save
# Three lines above can be replaced with
# #user.create_avatar(params)
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#avatar = Avatar.find(params[:id])
#avatar.user = #user
#avatar.destroy
redirect_to user_path(#user)
end
private
def avatar_params
params.require(:avatar).permit(:avatarpic)
end
end
There is only 1 photo according to my user model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :places
has_many :comments, dependent: :destroy
has_one :avatar, dependent: :destroy
end
Code for User's view profile page:
<%= link_to user_avatar_path(#user), :title => "Upload New Photo", method: :delete, data: { confirm: 'Are you sure you want to change your Photo?' } do %>
<%= image_tag #user.avatar.avatarpic %>
<% end %>
rake routes:
root GET / places#index
place_comments POST /places/:place_id/comments(.:format) comments#create
place_photos POST /places/:place_id/photos(.:format) photos#create
places GET /places(.:format) places#index
POST /places(.:format) places#create
new_place GET /places/new(.:format) places#new
edit_place GET /places/:id/edit(.:format) places#edit
place GET /places/:id(.:format) places#show
PATCH /places/:id(.:format) places#update
PUT /places/:id(.:format) places#update
DELETE /places/:id(.:format) places#destroy
user_avatars POST /users/:user_id/avatars(.:format) avatars#create
user_avatar DELETE /users/:user_id/avatars/:id(.:format) avatars#destroy
user GET /users/:id(.:format) users#show
routes.rb:
Rails.application.routes.draw do
devise_for :users
root 'places#index'
resources :places do
resources :comments, only: :create
resources :photos, only: :create
end
resources :users, only: [:show] do
resources :avatars, only: [:create]
resources :avatars, only: [:destroy]
# or resources :avatars, only: [:create, :destroy]
end
end
Avatar Model:
class Avatar < ApplicationRecord
belongs_to :user
mount_uploader :avatarpic, AvatarUploader
end
Thank you for your help.
Your link_to is missing the avatar_id. Change
user_avatar_path(#user)
To
user_avatar_path(#user, #user.avatar)
Hey there,
I am fairly new to Rails and I've managed to create a Favorite controller for my Items(Tools) and Users. I am displaying all Favorited Items(Tools) by a user correctly on his index. On the search view I provide links to favorite and unfavorite, but I am getting an error when I click on this link of a certain Item(Tool)
I am getting this error in the browser when favoriting an item:
ActiveRecord::AssociationTypeMismatch in ToolsController#favorite
Tool(#46153692) expected, got NilClass(#20297664)
The Request Parameters
{"_method"=>"put",
"authenticity_token"=>"vlWYHcp1K4Eu8WzjyEM8f6Eta9MNjgojtkr6RlG6n7121PGiWtXU8kDq9yXOfzGzw5grSc4GCqlcoK1UiLEsng==",
"type"=>"favorite", #WhatMyUserDid
"id"=>"1"} #MyUserId
My goal is to add a favorited Item(Tool) for a User (=> Error), show favorited Items(Tools) on Users index view (works fine), and show the link to Favorite or Unfavorite depending on the Favorites of the current_user (not implemented yet)
Here is my code:
app/models/favorite_tool.rb
class FavoriteTool < ActiveRecord::Base
belongs_to :tool
belongs_to :user
end
app/models/tool.rb
class Tool < ActiveRecord::Base
belongs_to :user
# Favorited by users
has_many :favorite_tools # just the 'relationships'
has_many :favorited_by, through: :favorite_tools, source: :user # the actual users favoriting a tool
mount_uploader :cover_filename, CoverUploader
end
app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :tools
# Favorite tools of user
has_many :favorite_tools # just the 'relationships'
has_many :favorites, through: :favorite_tools, source: :tool # the actual tools the user favorites
mount_uploader :avatar_filename, AvatarUploader
end
app/controllers/tools_controller.rb
class ToolsController < ApplicationController
before_action :find_tool, only: [:show, :edit, :update, :destroy]
# Add and remove favorite recipes
# for current_user
def favorite
type = params[:type]
if type == "favorite"
current_user.favorites << #tool
redirect_to :back, notice: 'You favorited #{#tool.title}'
elsif type == "unfavorite"
current_user.favorites.delete(#tool)
redirect_to :back, notice: 'Unfavorited #{#tool.title}'
else
# Type missing, nothing happens
redirect_to :back, notice: 'Nothing happened.'
end
end
def index
#favorites = current_user.favorites
#tools = Tool.where(user_id: current_user).order("created_at DESC")
#user = current_user
end
def search
#tool = Tool.find(1)
#tools = Tool.all.order("created_at DESC")
end
def show
end
def new
#tool = current_user.tools.build
end
def create
#tool = current_user.tools.build(tool_params)
if #tool.save
redirect_to tools_path
else
render 'new'
end
end
def edit
end
def update
if #tool.update(tool_params)
redirect_to tools_path
else
render 'edit'
end
end
def destroy
#tool.destroy
redirect_to tools_path
end
private
def find_tool
#tool = Tool.find(params[:id])
end
def tool_params
params.require(:tool).permit(:title, :subtitle, :url, :cover_filename)
end
end
app/views/tools/index.html.haml
%h2 My Favorite Tools
- #favorites.each do |tool|
= image_tag tool.cover_filename.url
%h2= link_to tool.title, tool
%p= tool.subtitle
%p= link_to "Edit", edit_tool_path(tool)
%p= time_ago_in_words(tool.created_at)
app/views/tools/search.html.haml
- #tools.each do |tool|
= image_tag tool.cover_filename.url
%h2= link_to tool.title, tool
%p= tool.subtitle
%p= link_to tool.user.try(:username), '/users/'+tool.user_id.to_s
%p= link_to "Favorite", favorite_tool_path(#tool, type: "favorite"), method: :put
%p= link_to "Unfavorite", favorite_tool_path(#tool, type: "unfavorite"), method: :put
%p= link_to "Edit", edit_tool_path(tool)
%p= time_ago_in_words(tool.created_at)
app/config/routes.rb
resources :tools do
put :favorite, on: :member
end
I hope the provided data is enough, if not please tell me. I'm grateful for all Your replies.
Your before_action needs to include :favorite as you are not defining the #tool in your favorite action.
I am using Devise gem in my Rails project. The reason I use Devise is because I want just logged in user can rate their teachers. Now, I get this error in my ratings_controller.rb although I already added user_id into my ratings and teachers table.
undefined method `current_user' for Teacher:0x00000004fcac48
#rating = #teacher.current_user.ratings.build
Here is my ratings_controller.rb:
class RatingsController < ApplicationController
def new
get_teacher
#rating = #teacher.current_user.ratings.build
end
def create
get_teacher
#rating = #teacher.current_user.ratings.create(rating_params)
if #rating.save
redirect_to school_teacher_path(#teacher.school, #teacher)
else
render 'new'
end
end
def destroy
get_teacher
#rating = #teacher.ratings.find(params[:id])
#rating.destroy
redirect_to school_teacher_path(#teacher.school, #teacher)
end
def get_teacher
#teacher = Teacher.find(params[:teacher_id])
end
private
def rating_params
params.require(:rating).permit(:easiness, :helpfulness, :clarity, :comment,
:teacher_id, :school_id)
end
end
rating.rb:
class Rating < ActiveRecord::Base
belongs_to :teacher
belongs_to :user
end
teacher.rb:
class Teacher < ActiveRecord::Base
belongs_to :school
has_many :ratings, dependent: :destroy
has_many :users
def name
"#{firstName} #{middleName} #{lastName}"
end
def to_s
name
end
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :ratings
has_many :teachers
end
current_user is a controller helper, it's not a instance method of model.
you can add before_action :authenticate_user! in controller to make sure only logged in user can rate
You can change several code here. Before fixing your code, I suggest you to user before_action to make DRY code.
class RatingsController < ApplicationController
before_action :authenticate_user!
before_action :get_teacher
# your code #
private
def get_teacher
#teacher = Teacher.find(params[:teacher_id])
end
def rating_params
params.require(:rating).permit(:easiness, :helpfulness, :clarity, :comment, :teacher_id, :school_id)
end
end
Then, add before_action :authenticate_user! above before_action :get_teacher so you can get current_user in each method.
Finally, you have to fix your new and create method into this:
def new
#rating = current_user.ratings.build
end
def create
#rating = current_user.ratings.create(rating_params)
if #rating.save
redirect_to school_teacher_path(#teacher.school, #teacher)
else
render 'new'
end
end
You do not need to #teacher.current_user.ratings.create(rating_params) to get teacher_id because you have teacher_id in your rating_params. I hope this help you.
You need to build rating object for user like this:-
#rating = current_user.ratings.build
I am trying to show a list of user's books,but for some reason the saved books won't appear on the view.The application is supposed to allow a signed in user to save books and then show list of books saved by that particular user. Here is my code..
<h1>Home#index</h1>
<%=link_to 'add a book',new_book_path%>
<ul>
<% #books.each do |b|%>
<li><%= b.title %></li>
<li><%= b.author %></li>
<%end%>
</ul>
BOOK CONTROLLER
class BooksController < ApplicationController
def index
end
def create
#book=current_user.books.build(params[:book])
if #book.save
redirect_to '/home/index'
else
render :action =>'new'
end
end
def new
#book=Book.new
end
end
Home controller
class HomeController < ApplicationController
#kip_before_filter :authenticate_user!
#efore_filter :homenticate_user!
def index
#books=current_user.books
end
def show
end
def welcome
end
end
Book model
class Book < ActiveRecord::Base
has_many :book_ownerships
has_many :users,:through => :book_ownerships
end
User model
class User < ActiveRecord::Base
has_many :book_ownerships
has_many :books,:through => :book_ownerships
has_many :skills
# Include default devise modules. Others available are:
# :token_authenticatable, :lockable, :timeoutable and :activatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation,:firstname
end
Book ownership model
class BookOwnership < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
Book view
<h1>Book#new</h1>
<p>Find me in app/views/book/index.html.erb</p>
<%= form_for(#book) do |f|%>
<%=f.text_field :title%>
<%=f.text_field :author%>
<%=f.submit%>
<%end%>
I assume that the first view in your example actually is Books#index and not Home#index and if that is true then you need to set the variable #books to something in the controller.
It should be enough to make the index action in your BooksController look like this:
def index
#books = current_user.books
end
At least if you are using Rails 3. If you are on an earlier version then you should add the call to the all method like this:
#books = current_user.books.all