Rails Activerecord Create or Find

Rails - find or create - is there a find or build?

Try XXX.find_or_initialize_by_uuid(XXXX)

Rails ActiveRecord Create or Find

The Rails 4.0 release notes denote that find_by_ has not been deprecated:

All dynamic methods except for find_by_... and find_by_...! are
deprecated.

Additionally, according to the Rails 4.0 documentation, the find_or_create_by method is still available, but has been rewritten to conform to the following syntax:

@picture = current_picture.posts.find_or_create_by(post_id: params[:id])

UPDATE:

According to the source code:

# rails/activerecord/lib/active_record/relation.rb
def find_or_create_by(attributes, &block)
find_by(attributes) || create(attributes, &block)
end

Thus, it stands to reason that multiple attributes can be passed as arguments to find_or_create_by in Rails 4.

rails ActiveRecord find or create

If you are using rails 4, you can do

@cart
.cart_items
.where(quantity: 0, price: @product.price, product_id: @product.id)
.first_or_initialize

you can also use find_or_create if you want to create the object in the database

If you are using rails 3 or lower , you can do

@cart
.cart_items
.find_or_initialize_by_quantity_and_price_and_product_id(0, @product.price, @product.id)

you can also use find_or_create_by_quantity_and_price_and_product_id if you want to create the object in the database

Rails create or update magic?

Rails 6

Rails 6 added an upsert and upsert_all methods that deliver this functionality.

Model.upsert(column_name: value)

[upsert] It does not instantiate any models nor does it trigger Active Record callbacks or validations.

Rails 5, 4, and 3

Not if you are looking for an "upsert" (where the database executes an update or an insert statement in the same operation) type of statement. Out of the box, Rails and ActiveRecord have no such feature. You can use the upsert gem, however.

Otherwise, you can use: find_or_initialize_by or find_or_create_by, which offer similar functionality, albeit at the cost of an additional database hit, which, in most cases, is hardly an issue at all. So unless you have serious performance concerns, I would not use the gem.

For example, if no user is found with the name "Roger", a new user instance is instantiated with its name set to "Roger".

user = User.where(name: "Roger").first_or_initialize
user.email = "email@example.com"
user.save

Alternatively, you can use find_or_initialize_by.

user = User.find_or_initialize_by(name: "Roger")

In Rails 3.

user = User.find_or_initialize_by_name("Roger")
user.email = "email@example.com"
user.save

You can use a block, but the block only runs if the record is new.

User.where(name: "Roger").first_or_initialize do |user|
# this won't run if a user with name "Roger" is found
user.save
end

User.find_or_initialize_by(name: "Roger") do |user|
# this also won't run if a user with name "Roger" is found
user.save
end

If you want to use a block regardless of the record's persistence, use tap on the result:

User.where(name: "Roger").first_or_initialize.tap do |user|
user.email = "email@example.com"
user.save
end

ActiveRecord::Relation create vs find_or_create_by

find_or_create_by quite simply first tries to find_by the arguments and if it cannot, passes them right along to create.

Meaning that the problem isn't with create, but rather in find_by. The conversion between symbol/string and integer that enum provides happens when getting/setting attributes on the model, which find_by does not do. find_by is simply not aware of the functionality provided by enum.

Your second method above is a decent way of accomplishing what you want. If pretty concerns you (and long method names don't) you could always wrap it in something like:

def self.find_or_create_by_with_my_type(type, hash)    
find_or_create_by hash.merge(my_type: my_types[type])
end

Rails - How to use Find Or Create

Related topic:

find_or_create_by in Rails 3 and updating for creating records

You can extend ActiveRecord with your own update_or_create method (see related topic) and then you can use this

@permission = Permission.update_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id) do |p|
p.group_id = @group.id
end

Or you can use find_or_create_by... method:

@permission = Permission.find_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id)
@permission.group = @group
@permission.save

Rails find_or_create_by more than one attribute?

Multiple attributes can be connected with an and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(use find_or_initialize_by if you don't want to save the record right away)

Edit: The above method is deprecated in Rails 4. The new way to do it will be:

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create

and

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize

Edit 2: Not all of these were factored out of rails just the attribute specific ones.

https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

Example

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

became

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

Rails ActiveRecord: How do I know if find_or_create_by found or created?

I don't believe there's a way to tell if the object is newly created or was already there. You could use find_or_initialize_by_widgetid instead which doesn't save the new object. You can then check widget.new_record? which will tell you whether the object has been saved or not. You'd have to put a save call in the block of code for a new object but as you want to make that check anyway it shouldn't ruin the flow of the code.

So:

widget = find_or_initialize_by_widgetid(:widgetid => "12345", :param2 => "folk")
if widget.new_record?
widget.save!
# Code for a new widget
else
# Code for an existing widget
end


Related Topics



Leave a reply



Submit