Is it possible in Rails to add an association to an existing record without immediately committing this change to the database? E.g. if I have Post has_many :ta
post_tag = post.post_tags.find_or_initialize_by_tag_id(Tag.first.id)
post_tag.save
This should work in Rails 3.2 and Rails 4:
post.association(:tags).add_to_target(Tag.first)
See this gist: https://gist.github.com/betesh/dd97a331f67736d8b83a
Note that saving the parent saves the child and that child.parent_id is NOT set until you save it.
EDIT 12/6/2015: For a polymorphic record:
post.association(:tags).send(:build_through_record, Tag.first)
# Tested in Rails 4.2.5
PREFACE This is not exactly an answer to this question but somebody searching for this kind of functionality may find this useful. Consider this and other options very carefully before wantonly putting it into a production environment.
You can, in some cases, leverage a has_one relationship to get what you want. Again, really consider what you're trying to accomplish before you use this.
Code to Consider
You have a has_many
relationship from Trunk
to Branch
and you want to add a new branch.
class Trunk
has_many :branches
end
class Branch
belongs_to :trunk
end
I can also relate them to each other singularly. We'll add a has_one
relationship to the Trunk
class Trunk
has_many :branches
has_one :branch
end
At this point, you can do things like Tree.new.branch = Branch.new
and you'll be setting up a relationship that will not save immediately, but, after saving, will be available from Tree.first.branches
.
However, this makes for quite a confusing situation for new developers when they look at the code and think, "Well, which the hell is it supposed to be, one or many?"
To address this, we can make a more reasonable has_one
relationship with a scope
.
class Trunk
has_many :branches
# Using timestamps
has_one :newest_branch, -> { newest }, class_name: 'Branch'
# Alternative, using ID. Side note, avoid the railsy word "last"
has_one :aftmost_branch, -> { aftmost }, class_name: 'Branch'
end
class Branch
belongs_to :trunk
scope :newest, -> { order created_at: :desc }
scope :aftmost, -> { order id: :desc }
end
Be careful with this, but it can accomplish the functionality being asked for in the OP.
To add to Isaac's answer, post.association(:tags).add_to_target(Tag.first)
works for has_many
relationships, but you can use post.association(:tag).replace(Tag.first, false)
for has_one
relationships. The second argument (false
) tells it not to save; it will commit it to the database by default if you leave the argument empty.