Rails ActiveRecord:Three Table has_many through:associations

我正在尝试构建一个表来处理某个广告系列已使用以下模型关联设置的位置和类别:

class Campaign < ActiveRecord::Base has_many :campaign_category_metro_bids, dependent: :destroy has_many :metros, through: :campaign_category_metro_bids has_many :categories, through: :campaign_category_metro_bids end class Metro < ActiveRecord::Base has_many :campaign_category_metro_bids has_many :campaigns, through: :campaign_category_metro_bids has_many :categories, through: :campaign_category_metro_bids end class Category < ActiveRecord::Base has_many :campaign_category_metro_bids has_many :campaigns, through: :campaign_category_metro_bids has_many :metros, through: :campaign_category_metro_bids end class CampaignCategoryMetroBid < ActiveRecord::Base belongs_to :campaign belongs_to :category belongs_to :metro end 

在尝试创建用于选择两个不同城市和类别的广告系列时,其中一个参数的ID的结果为NULL,如下所示:

在此处输入图像描述

广告系列制作代码

 def new if signed_in? # create new campaign @user = User.find(params[:id]) @campaign = @user.campaigns.new else redirect_to signin_path end end def create @campaign = User.find(params["campaign"]["user_id"]).campaigns.build(campaign_params) if @campaign.save flash[:success] = "Campaign created!" redirect_to current_user else render 'new' end end 

更新创建广告系列的视图使用两个单独的类别和地铁的collection_select作为:

   

   

campaigns_params:

  def campaign_params params.require(:campaign).permit(:name, :campaign_category_metro_bid_id, :metro_ids => [], :category_ids => []) end 

有没有更好的方法允许在我尝试时创建3表关系? 或者在选择时链接CategoryMetro模型的方法,以便在创建广告系列时生成的结果如下所示:

在此处输入图像描述

如果要确保跨多个表的数据一致性,我将从validation开始。 例如:

 class CampaignCategoryMetroBid < ActiveRecord::Base belongs_to :campaign belongs_to :category belongs_to :metro validates :campaign, presence: true validates :category, presence: true validates :metro, presence: true end 

如果您需要,也可能需要将此约束添加到数据库中(请参阅迁移指南)。 这样,没有人能够打破一致性,甚至没有破坏db cli。 现在,每当您的代码尝试创建没有外键的CampaignCategoryMetroBid实例时,ActiveRecord就会大喊大叫,并限制其余代码“行为”。

如果您真的只想注入一些默认外键(或以其他方式调整表单数据),您可以在控制器操作中预处理表单数据时执行此操作。 例如:

 class CampaignsController < ActionController::Base def create # Possibly refactor to a before_action if params[:campaign] && params[:capmaign][:metro_ids] && params[:capmaign][:metro_ids].empty? params[:campaign][:metro_ids] = [DEFAULT_CAMPAIGN_ID] end # Do the rest end end 

我希望这有助于大致设定方向:-)