Rails ActiveRecord:三个表 has_many 通过:关联

Posted

技术标签:

【中文标题】Rails ActiveRecord:三个表 has_many 通过:关联【英文标题】:Rails ActiveRecord: Three Table has_many through: associations 【发布时间】:2014-09-23 00:42:32 【问题描述】:

我正在尝试构建一个表格来处理某个活动已设置为具有以下模型关联的位置和类别:

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

更新 创建活动的视图对 Category 和 Metro 使用两个单独的 collection_select:

        <%= f.collection_select :category_ids, Category.all, :id, :display_category, , multiple: true %>

    <%= f.collection_select :metro_ids, Metro.all, :id, :full_name, , multiple: true %>

campaigns_params:

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

有没有更好的方法来允许创建 3 表关系,因为我正在尝试? 或在选择时链接CategoryMetro 模型的方法,以便在创建活动时生成的表格如下所示:

【问题讨论】:

您可以从控制器添加您的广告系列创建代码吗? @Eric 您还需要其他信息吗? 是的,你的campaign_params方法:) 好的,我补充了。有没有办法链接MetrosCategory 的选择所以不会出现NULL? 我会考虑创建两个关系表并分别填充它们 【参考方案1】:

如果您想确保跨多个表的数据一致性,我将从验证开始。例如:

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

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

【讨论】:

这是否有助于防止此处出现的情况:i.stack.imgur.com/u4TyA.png 我希望将MetrosCategory 的排列选为行中,而不是空值在图像中看到,即。对于category_id 下id 为9,10 的行,它应该列为12,而不是NULL 我不希望有任何默认的外键,但允许像我演示的那样选择。我需要添加什么? 是的,首先,良好的验证将确保无论您的代码库的其余部分如何处理,这种情况都不会再次发生。这会让你保持一致性。 然后下一步是在您的视图中创建一个下拉菜单(选择框)。用户只需选择一个值(如果您想确保始终选择某些内容,您可能应该禁用空白值)并且该值将映射到将在他提交表单时在参数中发送的 id(外键) .在这里,我会向您指出 Rails 指南,因为他们很好地解释了这一点 - guides.rubyonrails.org/… 是的,我已经在我更新的帖子中看到了collection_select 设置。如何将 Category 和 Metro 两者链接在一起,以便它们填充表格,就像我演示的那样

以上是关于Rails ActiveRecord:三个表 has_many 通过:关联的主要内容,如果未能解决你的问题,请参考以下文章

Rails 中的表关联 - ActiveRecord

如何编写迁移以重命名 Rails 中的 ActiveRecord 模型及其表?

使用 ActiveRecord 语法从 Rails 中的连接表中选择或按列排序

Rails ActiveRecord_Associations_CollectionProxy 与单表继承

Rails,ActiveRecord,Squeel:检查数组中是不是存在值

Rails HABTM 关联:记录未插入连接表