Reputation: 457
I have two models, users and promotions. The idea is that a promotion can have many users, and a user can have many promotions.
class User < ActiveRecord::Base
has_and_belongs_to_many :promotions
end
class Promotion < ActiveRecord::Base
has_and_belongs_to_many :users
end
I also have a promotions_users table/model, with no id of its own. It references user_id and promotions_id
class PromotionsUsers < ActiveRecord::Base
end
So, how do I add a user to a promotion? I've tried something like this:
user = User.find(params[:id])
promotion = Promotion.find(params[:promo_id])
promo = user.promotions.new(promo)
This results in the following error:
NoMethodError: undefined method `stringify_keys!' for #<Promotion:0x10514d420>
If I try this line instead: promo= user.promotions.new(promo.id)
I get this error:
TypeError: can't dup Fixnum
I'm sure that there is a very easy solution to my problem, and I'm just not searching for the solution the right way.
Upvotes: 30
Views: 23714
Reputation: 1013
For all those in the current times, Rails does have built-in functions that are like simpler associations.
For building (i.e. Promotion.new), you can use
user.promotions.build(promotion_attributes)
For creating it's the same
user.promotions.create(promotion_attributes)
Just wanted to give a more familiar option. It's outline in the apidoc The other answers work as well.
Upvotes: 0
Reputation: 11
If you want to add a User to a Promotion using a prototypical PromotionsController CRUD setup and you're not using Rails form helpers, you can format the params as:
params = {id: 1, promotion: { id: 1, user_ids: [2] }}
This allows you to keep the controller slim, e.g., you don't have to add anything special to the update
method.
class PromotionsController < ApplicationController
def update
promotion.update(promotion_params)
# simplified error handling
if promotion.errors.none?
render json: {message: 'Success'}, status: :ok
else
render json: post.errors.full_messages, status: :bad_request
end
end
private
def promotions_params
params.require(:promotion).permit!
end
def promotion
@promotion ||= Promotion.find(params[:id])
end
end
The result would be:
irb(main)> Promotion.find(1).users
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 2 ...>]>
Upvotes: 1
Reputation: 8461
You can do just
User.promotions = promotion #notice that this will delete any existing promotions
or
User.promotions << promotion
You can read about has_and_belongs_to_many
relationship here.
Upvotes: 11
Reputation: 667
This is also useful
User.promotion.build(attr = {})
so, promotion object saves, when you save User object.
And this is
User.promotion.create(attr = {})
create promotion you not need to save it or User model
Upvotes: 10
Reputation: 5101
user = User.find(params[:id])
promotion = Promotion.find(params[:promo_id])
user.promotions << promotion
user.promotions
is an array of the promotions tied to the user.
See the apidock for all the different functions you have available.
Upvotes: 54