Ruby On Rails, Design, Simplicity, Web 2.0, Ajax, Mac and Tons of Pizza.

Apr 17

Rake db:bootstrap and foreign keys

Posted by Sandro Paganotti in Ruby on Rails - comments are closed digg this add to delicious

While working on our current project I decided to implement, for the initial SQL import, the db:bootstrap task from technoweenie forum but I found some problems due to this fact:

The data is imported ordered by the name of the tables

This means that if you have a table named ‘accounts’ and a table named ‘users’ accounts data will be loaded first and users data will follow, but what happens if there is a foreign key between users table and accounts table ? ( eg: there is a column named user_id in the accounts table and you’re using the foreign key migrations plugin, or you could have scripted one foreign key in your migration files ) Well… obviously your database engine will return an error :-)

To avoid this I’ve changed a bit the bootstrap.rake file from Weenie in order to let you specify the import sequence by adding a 2 digit number in front of your filename (eg: “accounts.yml—> 10_accounts.yml”). This is the source code of the task, I know it’s quite dirty but it do its job.


   namespace :bootstrap do
      desc "Load initial database fixtures (in db/bootstrap/*.yml) into the current environment's database. Load specific fixtures using FIXTURES=x,y" 
      task :load => :environment do
        require 'active_record/fixtures'
        ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
        (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'db', 'bootstrap', '*.{yml,csv}'))).sort.each do |fixture_file|

          table_names = File.basename(fixture_file, '.*')
          file_names = table_names
          class_names = {}
          fixtures_directory = 'db/bootstrap'

          table_names =  table_names[3..table_names.length]          
          table_names = [table_names].flatten.map { |n| n.to_s }

          connection = ActiveRecord::Base.connection
          ActiveRecord::Base.silence do
            fixtures_map = {}
            fixtures = table_names.map do |table_name|
              fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, file_names.to_s))
            end               
            Fixtures.all_loaded_fixtures.merge! fixtures_map  

            connection.transaction(Thread.current['open_transactions'] == 0) do
              fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
              fixtures.each { |fixture| fixture.insert_fixtures }

              # Cap primary key sequences to max(pk).
              if connection.respond_to?(:reset_pk_sequence!)
                table_names.each do |table_name|
                  connection.reset_pk_sequence!(table_name)
                end
              end
            end

          end

        end
     end

I hope you’ll find this usefull :-)

Sandro

Post a comment

Categories:

Tags:

Powered by Mephisto, Valid XHTML 1.1, Valid CSS - Supported by Wave Factory