需要帮助来理解`has_and_belongs_to_many`

时间:2014-07-30 09:54:58

标签: ruby-on-rails ruby activerecord ruby-on-rails-4 has-and-belongs-to-many

数据库中有三个表:

  1. 用户(非空)
  2. 时间表(非空)
  3. schedules_users( empty
  4. 用户模型:

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


    时间表模型:

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


    welcome-controller:(我希望按日期和时间对日程安排进行排序)

    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这不正确,不是吗?
    SQL-输出:

    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)
    


    欢迎视图:

    <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>
    


    查看/ HTML输出:

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


    数据库:(schedules_users)
    enter image description here

    说明
    如你所见,我改变了has_many&amp;的关系。 belongs_tohas_and_belongs_to_many
    没有错误或例外 一切都应该是正确的(我不知道欢迎控制器中的排序命令是否正确)
    SQL输出看起来也是正确的
    如何将数据添加到schedules_users-table?

    如何访问schedules_users-table中添加的数据?
    我需要一个名为ScheduleUsers的新模型,还是......? 我不理解它。
    如果有人能帮助我会很好。
    感谢。

    <小时/>
    编辑:(解决方案[Rich Peck])
    时间表控制器:

    # 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
    


    欢迎视图:

    <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 %>
    



    如果我添加一个计划,一切正常。编辑也很好。
    如果我删除了一个计划,它将不会删除Join-Table中的关系 任何想法为什么? - &gt;解决方案:@schedule_delete.DESTROY而不是@schedule_delete.delete。因此,删除/销毁时间表也可以正常工作
    所以一切正常。
    感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

让我为您解释has_and_belongs_to_many

HABTM 基本上是ActiveRecord为您提供join table的一种方式 - 您的关联使您能够加载所需的关联数据通过您的加入表

has_many :through非常相似,HABTM提供了一种访问模型关联数据的简单方法,但由于它没有模型,因此无法直接访问连接数据(因为没有primary key加载它们):

enter image description here

它基本上使用了连接表的foreign_keys,并允许您从任一依赖模型中提取相关数据。你不能设置


如何将数据添加到schedules_users-table?

您必须使用collection ActiveRecord methods,特别是您必须确保能够将记录追加到schedulesusers协会:

#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

-

如何访问schedules_users-table中添加的数据?

您只能通过集合关联访问此数据

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

要从您的控制器访问,您将运行完全相同的命令

答案 1 :(得分:0)

看起来很快,但对我来说看起来很不错。但是,由于您已将模型从has_many更改为has_and_belongs_to_many,因此如果希望显示旧数据,则需要迁移数据。现在您没有为您的用户提供任何计划,这是正确的,因为schedules_users表为空。假设您的日程表中仍然有user_id列,您可以执行类似迁移数据的操作:

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