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