Rails: Has and belongs to many (HABTM) -- create association without creating other records
Asked Answered
P

3

15

Spent all day on Google, but can't find an answer. :\

I have a HABTM relationship between Users and Core_Values.

class CoreValue < ActiveRecord::Base
  has_and_belongs_to_many :users

class User < ActiveRecord::Base
  has_and_belongs_to_many :core_values

In my controller, I need to do two separate things:

  1. If a CoreValue does not exist, create a new one and associate it with a given user id, and
  2. Assuming I know a particular CoreValue does exist already, create the association without creating any new CoreValues or Users

For # 1, I've got this to work:

User.find(current_user.id).core_values.create({:value => v, :created_by => current_user.id})

This creates a new CoreValue with :value and :created_by and creates the association.

For # 2, I've tried a few things, but can't quite seem to create the association ONLY.

Thanks for your help!

Psychoneurotic answered 19/1, 2011 at 0:13 Comment(0)
O
21

You can do this in a two-step procedure, using the very useful find_or_create method. find_or_create will first attempt to find a record, and if it doesn't exist, create it. Something like this should do the trick:

core_value = CoreValue.find_or_create_by_value(v, :created_by => current_user.id)
current_user.core_values << core_value

Some notes:

  • The first line will find or create the value v. If it doesn't exist and is created, it will set the created_by to current_user.id.
  • There's no need to do User.find(current_user.id), as that would return the same object as current_user.
  • current_user.core_values is an array, and you can easily add another value to it by using <<.

For brevity, the following would be the same as the code example above:

current_user.core_values << CoreValue.find_or_create_by_value(v, :created_by => current_user.id)
Obscuration answered 19/1, 2011 at 0:29 Comment(4)
Thanks! Didn't know core_values was an array -- and certainly didn't know adding to the array would create an INSERT. Sweet! Any way to only create the association if the record doesn't exist? I'll go look through the array methods, I guess? Maybe current_user.core_values.include?Psychoneurotic
@Psychoneurotic That might be one way to do it, yeah. For clarification, I feel I should add that core_values isn't always an array--as you know, you can call core_values.create which isn't a valid array method. A better definition would be that it "acts as an array."Obscuration
thanks for the follow-up. include? won't work. trying to figure out if I can find the new core_value id inside current_user.core_values... hmmmmPsychoneurotic
@Psychoneurotic You can also do current_user.core_value_ids, which will return an array of IDs. Then current_user.core_value_ids.include?(core_value.id) should definitely work.Obscuration
C
4

Add a method in your user model:

class User < ActiveRecord::Base
  def assign_core_value(v)
    core_values.find_by_name(v) || ( self.core_values << 
      CoreValue.find_or_create_by_name(:name => v, :created_by => self)).last 
  end
end

The assign_core_value method meets requirement 1,2 and returns the core value assigned to the user (with the given name).

Now you can do the following:

current_user.assign_core_value(v)

Note 1

Method logic is as follows:

1) Checks if the CoreValue is already associated with the user. If so no action is taken, core value is returned.

2) Checks if the CoreValue with the given name exists. If not creates the CoreValue. Associates the core value(created/found) with the user.

Corbie answered 19/1, 2011 at 1:38 Comment(2)
Thanks! Good idea to add this to the user model. Will this create the association if it already exists?Psychoneurotic
Updated my answer with some explanation. Take a look.Corbie
D
0

Active Record already gives you a method. In your case,

val = CoreValue.find_by_value(v)
current_user.core_values << val

You can also pass a number of objects at ones this way. All the associations will be created

Check this for more information

Defective answered 6/6, 2012 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.