Rails 6 : find_or_create_by on a has_many relationship

I wanted to find out if find_or_create_by method is available on a has_many relationship. Reading through the forum posts was a waste of time. Documentation was also lacking examples to illustrate the use. Looking the source code of Rails can also be time consuming. The best option is to use the Bug Report Templates
that Rails provides to quickly experiment. I customized the test case to include the awesome_print gem to poke the objects and figure out how the ActiveRecord API works.

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails"
  gem "sqlite3"
  gem 'byebug'
  gem 'awesome_print'
end

require "active_record"
require "minitest/autorun"
require "logger"
require "awesome_print"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :products, force: true do |t|
  end

  create_table :orders, force: true do |t|
    t.integer :product_id
    t.string :confirmation
  end
end

class Product < ActiveRecord::Base
  has_many :orders
end

class Order < ActiveRecord::Base
  belongs_to :product
end

class BugTest < Minitest::Test
  def test_association_stuff
    product = Product.create!
    product.orders << Order.create!(confirmation: '1234')

    assert_equal 1, product.orders.count

    product.orders.find_or_create_by(confirmation: '789')

    assert_equal 2, product.orders.count
    byebug
  end
end

I have also included the byebug gem to get the console loaded with all the required objects to play with. You can see the byebug session:

(byebug) ap product
#<Product id: 1>
(byebug) ap product.orders
D, [2018-07-09T19:37:14.277974 #10336] DEBUG -- :   Order Load (0.1ms)  SELECT  "orders".* FROM "orders" WHERE "orders"."product_id" = ? LIMIT ?  [["product_id", 1], ["LIMIT", 11]]
#<ActiveRecord::Associations::CollectionProxy [#<Order id: 1, product_id: 1, confirmation: "1234">, #<Order id: 2, product_id: 1, confirmation: "789">]>

So, the answer is, yes, ActiveRecord is capable of doing find_or_create_by on a has_many relationship. If you do a gem list, you will not find the Rails gem. So I added:

puts Rails.gem_version

to print the version, in my case it is: 6.0.0.alpha. I had to read the Rails source code to figure out how to print the gem version. You can quickly check if it works in a given Rails version by pointing the Rails gem version inside the gemfile block to a specific version.

Reference

Bug Report Templates


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.