Rails First_Or_Create Activerecord Method

Rails first_or_create ActiveRecord method

From the Guides

first_or_create

The first_or_create method checks whether first returns nil or not. If it does return nil, then create is called. This is very powerful when coupled with the where method. Let’s see an example.

Suppose you want to find a client named ‘Andy’, and if there’s none, create one and additionally set his locked attribute to false. You can do so by running:

Client.where(:first_name => 'Andy').first_or_create(:locked => false)
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">

The SQL generated by this method looks like this:

SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
COMMIT

first_or_create returns either the record that already exists or the new record. In our case, we didn’t already have a client named Andy so the record is created and returned.

first_or_create!

You can also use first_or_create! to raise an exception if the new record is invalid. Validations are not covered on this guide, but let’s assume for a moment that you temporarily add

validates :orders_count, :presence => true

to your Client model. If you try to create a new Client without passing an orders_count, the record will be invalid and an exception will be raised:

Client.where(:first_name => 'Andy').first_or_create!(:locked => false)
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank

Update value with first_or_create in rails

In you controller, make the changes

def change_like_status
if current_user
status = Like.create_or_change_status(params[:id], current_user.id)
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end

end

In your model like.rb file, add a method

def self.create_or_change_status(business_id, user_id)
status = where(business_id: business_id, user_id: user_id).first
if status.nil?
status = create({business_id: business_id, user_id: user_id, liked: 1})
else
status.update_attributes(liked: !status.liked)
end
status
end

Can I override create with first_or_create?

It is possible with monkey patching but it's a very bad idea. You would be asking for major trouble down the road when you or someone else expects the code to behave in a certain default way. What if the requirements change and you remove the validation? You would silently never be able to create multiple records with create because first_or_create would always find the existing one.

The best alternative would be to check if the role already exists in scene.roles. For example:

scene.roles.include?(role) ? false : scene.roles << role
# does scene.roles include role? if yes: do nothing, if not: add role

Or do something like this.

Difference between two different ways of using first_or_create in ActiveRecord

Actually, there are two methods: find_or_create_by, add one column and give a set of attributes.

So, for example you can write:

find_or_create_by_name('Willy', time_zone: 'UTC')

The second is first_or_create, and that works as you were suggested (your second solution) (see the documentation as well).

There was a suggestion to introduce the find_or_create method, which accepts a hash, but that resulted in the first_or_create. (see discussion here).

Note that both are also explained well in the relevant rails guide.

[UPDATE 2014-06-30] Note that currently the notation has been changed, we should now write

Organization.find_or_create_by(name: '37 signals') do |organisation|
organisation.time_zone = 'Central'
end

which will try to find the organisation by name, and if not found use the block to create the organisation. Note: the block is only executed if the organisation did not exist!

rails first_or_create does not update existing records

The first_or_create will only execute the block on creation. This is intentional, it allows you to set certain values initially and not overwrite them when the record already exists.

If you always want to execute the block, you can write it as follows:

Blob.where(user_id: user.id, item_id: item.id).first_or_create.tap do |s|
s.amount += amount
s.save
end

Or, I prefer the little shorter version

Blob.find_or_create_by(user_id: user.id, item_id: item.id).tap do |s|
s.amount += amount
s.save
end

Race conditions in Rails first_or_create

Yes, it's possible.

You can significantly reduce the chance of conflict with either optimistic or pessimistic locking. Of course optimistic locking requires adding a field to the table, and pessimistic locking doesn't scale as well--plus, it depends on your data store's capabilities.

I'm not sure whether you need the extra protection, but it's available.

first_or_create only insert

Use create_with by following:

c = Country.create_with(status: 'old').find_or_create_by(name: 'test')

This will create Country with status 'old' and name 'test' only and only if it doesn't find the country with name 'test'. If it does find the country with name 'test', it will not update the status.

Ultimately, it will also return the country in c, whether after finding or creating.

create_with only sets attributes when creating new records from a relation object.

first_or_create by email and then save the nested model

first_or_create accepts a block. So you could do it as follows:

@user = User.where(:email => params[:user][:email]).first_or_create do |user|
# This block is called with a new user object with only :email set
# Customize this object to your will
user.attributes = params[:user]
# After this, first_or_create will call user.create, so you don't have to
end


Related Topics



Leave a reply



Submit