0

I am currently trying to import over 40 CSV's exported from sqlite3 into oracle db but I seem to have issues whilst importing some of the CSV's, into the corresponding tables.

The code line with:

class_name.create!(row.to_hash)

produces errors on some classes because the callbacks are also triggered when the

.create!() method is called

def import_csv_into_db
  Dir.foreach(Rails.root.join('db', 'csv_export')) do |filename|
      next if filename == '.' or filename == '..' or filename == 'extract_db_into_csv.sh' or filename =='import_csv.rb'
      filename_renamed = File.basename(filename, File.extname(filename)).chomp('s').titleize.gsub(/\s+/, "")
        CSV.foreach(Rails.root.join('db', 'csv_export',filename), headers: true) do |row|
          class_name = Object.const_get(filename_renamed)
          puts class_name
          class_name.create!(row.to_hash)
          puts "Insert on table #{class_name}s complete with:  #{row.to_hash}"
      end
  end
end

The issue at hand is that my CSV import function is in the seeds.rb, so whenver I run bundle exec rake db:seed the CSV's are imported.

How exactly can I avoid the callbacks being triggered when class_name.create!(row.to_hash) is triggered within the function in the seeds.rb ?

In my customer.rb I have callbacks such as:

after_create :add_default_user or after_create :add_build_config

I'd like to manipulate my function within the seeds.rb to skip the callbacks when the function tries importing a CSV file like customers.csv (which would logically call Customer.create!(row.to_hash)).

Pro_grammer
  • 346
  • 2
  • 9
  • 1
    You can replace `.chomp('s').titleize.gsub(/\s+/, "")` with [`.gsub(/\s+/, "").classify`](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-classify) and `Object.const_get(filename_renamed)` with [`constantize`](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize). `Dir.foreach` can be replaced with [`Dir.children`](https://ruby-doc.org/core-2.7.4/Dir.html#method-c-children) which skips `.` and `..`. – Schwern Sep 14 '21 at 14:26

1 Answers1

3

There are lower level methods which will not run callbacks. For example, instead of create! you can call insert!. Instead of destroy you can call delete.

Side note: use insert_all! to bulk insert multiple rows at once. Your import will be much faster and it does not use validations. Though I would recommend the more flexible active-import instead.


However, skipping callbacks might cause problems if they are necessary for the integrity of the data. If you delete instead of destroy associated data may not be deleted, or you may get errors because of referential integrity. Be sure to add on delete actions on your foreign keys to avoid this. Then the database itself will take care of it.

Consider whether your db:seeds is doing too much. If importing this CSV is a hindrance to seeding the database, consider if it should be a separate rake task instead.

Consider whether your callbacks can be rewritten to be idempotent, that is to be able to run multiple times. For example, after_create :add_default_user should recognize there already is a default user and not try to re-add it.

Finally, consider whether callbacks which are run every time a model is created are the correct place to do this work.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • thank you! What difference does the exclamation mark in front of create! or insert! make? Thanks for all the tips, ill be sure to do some digging and adjustment :) @schwern – Pro_grammer Sep 14 '21 at 14:09
  • 1
    @Naj If the create or insert fails it will throw an exception rather than returning `nil`. You generally want to use the `!` version of all database methods and handle the exceptions. If you don't, your code will silently fail. – Schwern Sep 14 '21 at 14:11
  • Thanks. So i tried running it with insert! and got `NoMethodError: undefined method insert!' for #` What could the reason be for this? Why would create! work and insert! return a no method error? @schwern – Pro_grammer Sep 14 '21 at 14:14
  • 2
    @Naj [`insert!` was added in Rails 6](https://apidock.com/rails/ActiveRecord/Persistence/ClassMethods/insert%21). – Schwern Sep 14 '21 at 14:22
  • what do i do if i dont have rails 6, my siuation currently wont allow me to upgrade without ruining everything else, how do I skip callbacks in rails 5.2? – Pro_grammer Sep 15 '21 at 10:24
  • 1
    @Naj Use activerecord-import. Or redesign your callbacks and/or db:seed. Probably the second. – Schwern Sep 15 '21 at 14:33