Home > front end >  Ruby on Rails associating a User to another models text field
Ruby on Rails associating a User to another models text field

Time:10-24

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.

  • Related