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

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

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'

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|

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

class Product < ActiveRecord::Base
  has_many :orders

class Order < ActiveRecord::Base
  belongs_to :product

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

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.


Bug Report Templates

Related Articles

Create your own user feedback survey