Rails ActiveRecord: Three Table has_many through: associations

旧城冷巷雨未停 提交于 2019-12-21 23:45:59

问题


I am attempting to build a table to handle both the location and category a certain campaign has been set to with the following model 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

When attempting to create a campaign for selecting two different cities and categories the result is NULL for the id of one of the paramters as:

Campaign creation code:

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

UPDATED The view to create the campaign uses two separate collection_select for Category and Metro as:

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

and

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

Is there a better way to allow for the creation of a 3 table relation as I am attempting? or a way to link the Category and Metro models at selection so that the resultant table is something like below upon campaign creation:


回答1:


If you want to ensure data consistency across multiple tables I would begin with validations. For example:

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

You also might want to add this constraint to your db if that's what you need (see the migration guides). That way nobody will be able break the consistency, not even from the db cli. Now every time your code attempts to create a CampaignCategoryMetroBid instance without the foreign keys, ActiveRecord will shout and that constraints the rest of your code to "behave".

If you really just want to inject some default foreign key (or otherwise tweak the form data) you can do this while preprocessing the form data in your controller actions. For example:

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 hope this helps to generally set the direction :-)



来源:https://stackoverflow.com/questions/25985163/rails-activerecord-three-table-has-many-through-associations

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