Flowdock
method

first_or_create

Importance_5
v3.2.3 - Show latest stable - 5 notes - Class: ActiveRecord::Relation
first_or_create(attributes = nil, options = {}, &block) public

Tries to load the first record; if it fails, then create is called with the same arguments as this method.

Expects arguments in the same format as Base.create.

Examples

# Find the first user named Penélope or create a new one.
User.where(:first_name => 'Penélope').first_or_create
# => <User id: 1, first_name: 'Penélope', last_name: nil>

# Find the first user named Penélope or create a new one.
# We already have one so the existing record will be returned.
User.where(:first_name => 'Penélope').first_or_create
# => <User id: 1, first_name: 'Penélope', last_name: nil>

# Find the first user named Scarlett or create a new one with a particular last name.
User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
# => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>

# Find the first user named Scarlett or create a new one with a different last name.
# We already have one so the existing record will be returned.
User.where(:first_name => 'Scarlett').first_or_create do |user|
  user.last_name = "O'Hara"
end
# => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
Show source
Register or log in to add new notes.
November 6, 2014
7 thanks

Find the First Instance in the Table. If None Exists, Create One.

Specify the data you’re looking for. If it exists in the table, the first instance will be returned. If not, then create is called.

If a block is provided, that block will be executed only if a new instance is being created. The block is NOT executed on an existing record.

Code example

MyStat.where(name: statistic_name).first_or_create do |statistic|
  statistic.value = calculate_percentage
  statistic.statistic_type = "percentage"
end
January 22, 2016
3 thanks

Creates record by given attributes only if table is empty

This method first searches the table for ANY FIRST RECORD, not the one matching given attributes. If no record is found at all, it creates one using the specified attributes. This might be misunderstood in many cases.

March 23, 2016
1 thank

RE: RE: FALSE: Creates record by given attributes only if table is empty

keredson is still a little off on the behavior.

>> Where in reality this would grab the “first” record (say w/ email “john@smith.com”), and change its email to “derek@somwhere.com”, a fairly surprising action that doesn’t obviously fail, leading to some pretty subtle bugs.

This isn’t right, as if you look at the source it calls: “first || create(attributes, &block)”

So in the example of:

User.first_or_create(email: 'derek@somwhere.com')

it would find the first user with any email, and return it. And thats it. The attributes passed in as params are only used in the event that first returns no matches, and create is called.

I’m using it in a way similar to:

Foo.where(bar: baz_params[:bar]).first_or_create(baz_params)

This will find the first Foo where bar is equal to the bar sent from baz_params. If none is found, it will create it. It’s useful for me when importing large amounts of data where I know there will be duplicate records.

March 6, 2016
1 thank

RE: FALSE: Creates record by given attributes only if table is empty

Dino’s comment was almost certainly due to a misunderstanding of the API, a mistake I made myself the first time I used it, and one I’ve witnessed multiple others make as well.

The fact that this method takes an attributes parameter leads one to think the passed params are the criteria for the selection. At first glance it would appear it would be used like this:

User.first_or_create(email: 'derek@somwhere.com')

Where in reality this would grab the “first” record (say w/ email “john@smith.com”), and change its email to “derek@somwhere.com”, a fairly surprising action that doesn’t obviously fail, leading to some pretty subtle bugs.

In reality it should be used like this:

User.where(email: 'derek@somwhere.com').first_or_create

And the attributes param. isn’t used in 99% of use cases. (If at all - the provision for the block that executes only on create fully duplicates what the attributes parameter can do in a much more obvious way.)

IMHO this is simply a bad API that one just needs to be aware of. But it’s unfair to knock dino for what’s likely a highly common misreading.

March 4, 2016 - (>= v4.0.2)
0 thanks

FALSE: Creates record by given attributes only if table is empty

I very much doubt that dino’s comment was ever correct, but it certainly isn’t correct now. The behavior liantics describes is correct.