hello I have 2 models User.rb and Guest.rb.
in my app the user is responsible for entering the Guest information. I have a table in my views that will show all guests. I would like each guest row to display the user who has entered their information. Iam having some trouble properly setting up the current_user method in my controller methods. Currently iam grabbing the current_usera nd entering it next to every guest. Thank you so much in advance.
Controller:
def new
@guest = Guest.new
end
def create
@guest = Guest.new(guest_params)
if @guest.save
redirect_to guests_path
else
render 'new'
end
end
def index
@guests = Guest.all
@user = current_user
end
def show
@guest = Guest.find(params[:id])
@user = current_user
end
def edit
@guest = Guest.find(params[:id])
end
def update
@guest = Guest.find(params[:id])
if @guest.update(guest_params)
flash[:success] = "Profile updated"
redirect_to @guest
else
render 'edit'
end
end
def destroy
Guest.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to guests_url
end
def guest_params
params.require(:guest).permit(:experience,:interaction,:mood,:guest_name,:room_num,:arrival_date,:departure_date,:opportunity_string,:employee,:notes,:opportunity)
end
end
Models:
has_and_belongs_to_many :users
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_and_belongs_to_many :guests
end
Views:
body{background-color:white;}
</style>
<h1 >Guests</h1>
<div style="overflow-x: auto; mb-3">
<table >
<thead>
<tr style="background-color:#CFD2CF;font-size:1.4vw">
<th>GUEST</th>
<th>EXPERIENCE</th>
<th>ROOM</th>
<th>ARRIVAL</th>
<th>DEPARTURE</th>
<th>OPPORTUNITY</th>
<th>EMPLOYEE</th>
<th>DEPARTMENT</th>
</tr>
</thead>
<tbody>
<% @guests.each do |guest| %>
<tr style="background-color:<%=guest.mood%>">
<td> <%= link_to guest.guest_name, "/guests/#{guest.id}" %></td>
<td><%= guest.experience %></td>
<td><%= guest.room_num %></td>
<td><%= guest.arrival_date %></td>
<td><%= guest.departure_date %></td>
<td ><%= @user.current_user%></td>
<td><%= %></td>
<td><%= guest.interaction %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
Schema:
create_table "guests", force: :cascade do |t|
t.string "experience"
t.string "interaction"
t.string "mood"
t.string "guest_name"
t.string "room_num"
t.string "arrival_date"
t.string "departure_date"
t.string "opportunity_string"
t.string "employee"
t.string "notes"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id", null: false
t.index ["user_id"], name: "index_guests_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "superadmin_role", default: false
t.boolean "supervisor_role", default: false
t.boolean "user_role", default: true
t.string "name"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "guests", "users"
end
CodePudding user response:
A few things that will help:
Devise and current_user
:
Devise takes care of current_user
for you, so you should just be able to call current_user
in your controllers and views.
@user.current_user
isn't a method unless you've created one in your User
model, which I would not recommend doing.
Don't mix @user
with current_user
It's possible to set @user = current_user
, but I think it's a bad practice as it will get confusing quickly
@user
should be tied to the model User
and represent the user that the current_user
is interacting with.
For example, a URL like /users/1/edit
should set @user = User.find(1)
.
The current_user
could be someone else who is editing the @user
object.
HABTM Associations
Given an assigned @user
, you can call @user.guests
to get all guests associated with that user.
E.g. for a route that creates the following URL: /users/1/guests
then your controller can have something like this:
# users_controller.rb
class UsersController < ApplicationController
...
def guests
@user = User.find(params[:id])
@guests = @user.guests
end
end
And the reverse is true as well. For a route like guests/1/users
you can call @guest.users
.
But...
Do you really want a HABTM?
If a User
can create many Guests
, but a Guest
is never associated with many Users
, then this isn't really a "has and belongs to many" relationship. You probably just want a simple has_many belongs_to
.
I revisit this article often as a refresher when I'm considering my relationships.
Your schema has a user_id
on your guest
model, which indicates to me that you want to be able to say: "A User has_many guests. A Guest belongs_to a user."
But you wouldn't really say "A Guest can belong_to many Users"
Code fixes
I have a table in my views that will show all guests.
For ALL guests, this would be the URL /guests
, which should map to GuestsController#index
:
# guests_controller.rb
class GuestsController < ApplicationController
...
def index
@guests = Guest.all # or some scope like Guest.active
end
...
end
For guests related to a give user, this would be the URL /users/:id/guests
which should map to UsersController#guests
:
# users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: %i[show edit update guests]
...
def guests
@user.guests
...
end
...
private
# this method sets @user for all views defined in the `:only` hash of the `before_action` callback.
def set_user
@user = User.find(params[:id]
end
I would like each guest row to display the user who has entered their information.
Since you have a user_id
field on Guest
, if you switch to a has_many
belongs_to
relationship, then you can just call the user:
<tbody>
<% @guests.each do |guest| %>
<tr style="background-color:<%=guest.mood%>">
<td> <%= link_to guest.guest_name, "/guests/#{guest.id}" %></td>
<td><%= guest.experience %></td>
<td><%= guest.room_num %></td>
<td><%= guest.arrival_date %></td>
<td><%= guest.departure_date %></td>
<td ><%= guest.user%></td> <!-- guest.user instead of @user.current_user -->
<td></td>
<td><%= guest.interaction %></td>
</tr>
<% end %>
</tbody>
Extra credit: use Includes to pre-load associations
Also, as a pro tip, calling guest.user
could get slow because each guest
record needs to make a call to the User
table.
Rails offers eager loading for just this situation.
Change @guests = Guest.all
to @guest = Guest.includes(:user).all
and Rails will handle the rest.