Need help to understand `has_and_belongs_to_many`

烂漫一生 提交于 2020-02-25 07:49:27

问题


There are three tables in the database:

  1. users (not empty)
  2. schedules (not empty)
  3. schedules_users (empty)

user-model:

class User < ActiveRecord::Base
  [...]
  has_and_belongs_to_many :schedules#has_many :schedules
  [...]
end


schedule-model:

class Schedule < ActiveRecord::Base
  has_and_belongs_to_many :users#belongs_to :user
end


welcome-controller: (where I want to sort the schedules by date and time)

class WelcomeController < ApplicationController
  def index
    if(current_user)
      @user_schedules = current_user.schedules
      @user_schedules_date = @user_schedules.order(:date_time).group_by { |sched| sched.date_time.beginning_of_day }
    end
  end
end

@user_schedules = current_user.schedules this is not correct, isnt it?

SQL-output:

Started GET "/" for 127.0.0.1 at 2014-07-30 11:24:09 +0200
Processing by WelcomeController#index as HTML
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 12 LIMIT 1
  Schedule Load (1.0ms)  SELECT "schedules".* FROM "schedules" INNER JOIN "schedules_users" ON "schedules"."id" = "schedules_users"."schedule_id" WHERE "schedules_users"."user_id" = $1 ORDER BY "schedules"."date_time" ASC  [["user_id", 12]]
  Rendered welcome/index.html.erb within layouts/application (0.0ms)
Completed 200 OK in 47ms (Views: 43.0ms | ActiveRecord: 2.0ms)


welcome-view:

<div class="table-responsive">
  <table class="table table-hover">
    <tbody>
      <% @user_schedules_date.sort.each do |date_time, schedules| %>
        <tr class="thead success">
          <th colspan="4" scope="col"><p><%= date_time.strftime("%A, %d.%m.%Y") %></p></th>
        </tr>
        <% for schedule in schedules %>
          <tr>
            <th scope="row"><p><%= schedule.titel %></p></th>
            <td><p><%= schedule.date_time.strftime("%H:%M:%S") %></p></td>
            <td><p><%= schedule.location %></p></td>
          </tr>
        <% end %>
      <% end %>
    </tbody>
  </table>
</div>


view/html-output:

<div class="table-responsive">
  <table class="table table-hover">
    <tbody>
    </tbody>
  </table>
</div>


database: (schedules_users)



Description:
As you can see I changed the relationship from has_many & belongs_to to has_and_belongs_to_many.
There is no error or exception.
Everything should be right
(I dont know whether the sort command in the welcome-controller is correct anymore).
The SQL-output looks also correct.

How can I add data to the schedules_users-table?
AND
How can I get access to the added data in the schedules_users-table?
Do I need a new model called ScheduleUsers, or... ?
I dont understand it atm.
Would be nice if anyone can help me.
Thanks.



EDIT: (Solution [Rich Peck])
schedule-controller:
# encoding: UTF-8
class ScheduleController < ApplicationController
  def add
    if (current_user.nil?)
      redirect_to "/"
    else
      if (@schedule_add.nil?)
        @schedule_add = Schedule.new
      end
    end
  end

  def add_now
    @schedule_add = Schedule.new(schedule_params)
    if @schedule_add.valid?

      @schedule_add.save

      current_user.schedules << @schedule_add # ADD RELATIONSHIP TO THE JOIN-TABLE

      #UserMailer.welcome_email(@user).deliver #BestätigungsEmail versenden
      flash[:notice] = 'Ein neuer Termin wurde erstellt.'
      redirect_to :root
    else
      render :action => "add"
    end
  end

  def edit
    if (current_user.nil?)
      redirect_to "/"
    else
      if current_user.schedules.nil?
        redirect_to "/"
      else
        begin #try
        @schedule_edit = current_user.schedules.find(params[:id])
        rescue #catch
          if @schedule_edit.nil?
            flash[:notice] = 'Der Termin konnte nicht gefunden werden.'
            redirect_to :root
          end
        end
      end
    end
  end

  def edit_now
    @schedule_edit = current_user.schedules.find(params[:id])
    if @schedule_edit.nil?
      flash[:notice] = 'Der Termin konnte nicht gefunden werden.'
      redirect_to :root
    else
      @schedule_edit.update(schedule_params)
      if @schedule_edit.valid?

        @schedule_edit.save

        flash[:notice] = 'Der Termin '+@schedule_edit.titel+' wurde bearbeitet.'
        redirect_to :root
      else
        render :action => "edit"
      end
    end
  end

  def delete
    if (current_user.nil?)
      redirect_to "/"
    else
      if current_user.schedules.nil?
        redirect_to "/"
      else
        begin #try
          @schedule_delete = current_user.schedules.find(params[:id])
        rescue #catch
          if @schedule_delete.nil?
            flash[:notice] = 'Der Termin konnte nicht gefunden werden.'
            redirect_to :root
          end
        end
      end
    end
  end

  def delete_now
    begin #try
      @schedule_delete = current_user.schedules.find(params[:id])
    rescue #catch
      if @schedule_delete.nil?
        flash[:notice] = 'Der Termin konnte nicht gefunden werden.'
        redirect_to :root
      end
    end

    if @schedule_delete.nil?
      flash[:notice] = 'Der Termin konnte nicht gefunden werden.'
      redirect_to :root
    else
      @schedule_delete.delete #SHOULD DELETE RELATIONSHIP IN THE JOIN-TABLE, BUT DIDNT
      flash[:notice] = 'Der Termin '+@schedule_delete.titel+' wurde gelöscht.'
      redirect_to :root
    end
  end

  def invite
  end
  # ---------------------------------------------------------------------------
  private
  #allowed parameters to pass through to the model
  def schedule_params
    params.require(:schedule).permit(:id, :titel, :location, :date_time)
  end
end


routes.rb:

Calendar::Application.routes.draw do
  root 'welcome#index'

  get "welcome/index" => "welcome#index"

  get "auth/sign_up" => "auth#sign_up"
  get "auth/sign_in" => "auth#sign_in"
  get "auth/sign_out" => "auth#sign_out"
  get "auth/settings" => "auth#settings"
  get "auth/pwd_forgot" => "auth#pwd_forgot"
  get "auth/pwd_reset" => "auth#pwd_reset"

  post "auth/sign_in" => "auth#login"
  post "auth/sign_up" => "auth#register"
  put "auth/settings" => "auth#save_settings"
  put "auth/pwd_forgot" => "auth#send_pwd_reset_instructions"
  put "auth/pwd_reset" => "auth#new_pwd"

  delete "auth/settings" => "auth#delete"

  #resources :users, only: :index
  #resources :users do
  #  post   :add, action: :add_schedule #-> domain.com/users/4/schedules/add
  #  delete :remove, action: :remove_schedule #-> domain.com/users/4/schedules/remove
  #end

  get "schedule/add" => "schedule#add"
  post "schedule/add" => "schedule#add_now"
  get "schedule/invite" => "schedule#invite"
  #get "schedule/edit" => "schedule#edit"
  #get "schedule/edit/:id" => "schedule#edit"
  get '/schedule/edit/:id', to: 'schedule#edit', as: 'schedule/edit'
  put "schedule/edit/:id" => "schedule#edit_now" #PUT oder POST ?
  #get "schedule/delete" => "schedule#delete"
  get '/schedule/delete/:id', to: 'schedule#delete', as: 'schedule/delete'
  delete "schedule/delete/:id" => "schedule#delete_now"
end


welcome-view:

<div class="table-responsive">
      <table class="table table-hover">
        <tbody>
          <% @user_schedules_date.sort.each do |date_time, schedules| %>
            <!-- Datum -->
            <tr class="thead success">
              <th colspan="4" scope="col"><p><%= date_time.strftime("%A, %d.%m.%Y") %></p></th>
            </tr>
            <!-- /Datum_Ende -->

            <!-- Titel, Zeit und Ort -->
            <% for schedule in schedules %>
              <tr>
                <th scope="row"><p><%= schedule.titel %></p></th>
                <td><p><%= schedule.date_time.strftime("%H:%M:%S") %></p></td>
                <td><p><%= schedule.location %></p></td>
                <td>
                  <p><%= link_to 'Bearbeiten', {:controller => 'schedule', :action => 'edit', :id => schedule.id} %></p>
                  <p><%= link_to 'Diesen Termin löschen', {:controller => 'schedule', :action => 'delete', :id => schedule.id} %></p>
                </td>
              </tr>
            <% end %>
            <!-- Titel, Zeit und Ort -->
          <% end %>
        </tbody>
      </table>
    </div>
  <% end %>



If I add a schedule everything works fine. Edit also works fine.
If I delete a schedule it won't delete the relationship in the Join-Table.
Any Idea why? -> Solution: @schedule_delete.DESTROY instead of @schedule_delete.delete. So deleting/destroying a schedule also works fine.

So everything works fine.
Thank you for your help!


回答1:


Let me explain has_and_belongs_to_many for you

HABTM is basically a way for ActiveRecord to give you a join table - your association gives you the ability to load the associative data you require through your join table.

Much like has_many :through, HABTM provides a simple way to access the associative data of your models, however as it does not have a model, you will not be able to access the join data directly (as there's no primary key to load them with):

It basically takes the foreign_keys of your join table, and allows you to pull the associated data from either of the dependent models for you. You can't set


How can I add data to the schedules_users-table?

You'll have to use the collection ActiveRecord methods, specifically you'll have to ensure you are able to append records to the schedules or users associations:

#config/routes.rb
resources :users do
    post   :add, action: :add_schedule #-> domain.com/users/4/schedules/add
    delete :remove, action: :remove_schedule #-> domain.com/users/4/schedules/remove
end

#app/controllers/users_controller.rb
Class UsersController < ApplicationController
    before_action :set_schedule, only: [:add_schedule, :remove_schedule]
    def add_schedule
       @user.schedules << @schedule #-> has to be an ActiveRecord object
    end

    def remove_schedule
       @user.schedules.delete params[:schedule_id]
    end

    private

    def set_schedule
       @user = User.find params[:user_id]
       @schedule = Schedule.find params[:id]
    end
end

--

How can I get access to the added data in the schedules_users-table?

You can only access this data through the collection associations:

$ rails c
$ @user = User.find 1
$ @user.schedules.each do |schedule|
$   puts schedule #-> will output each schedule record
$ end

To access from your controller, you'll run exactly the same commands




回答2:


Just looked quickly, but looks pretty ok to me. However, since you changed your models from has_many to has_and_belongs_to_many you need to migrate your data if you want your old data to show up. Now you are getting no schedules for your users, this is correct since the schedules_users table is empty. Assuming you still have the user_id column in your schedules table you could do something like to migrate the data:

insert into schedules_users 
(select id as schedule_id, user_id from schedules where user_id is not null); 


来源:https://stackoverflow.com/questions/25033822/need-help-to-understand-has-and-belongs-to-many

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!