Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Arel not picking up on ActiveRecord establish_connection settings

#6491

Hi there,

in my 3.0.4 application I'm trying to set an individual databases for some of my models in the following way within the environment.rb:

models.each do |klass|
  next unless klass < ActiveRecord::Base
  klass.establish_connection "processing_#{Rails.env}"
end

When doing a Channel.connection.instance_eval {@config[:database]} afterwards (Channel being one of my models), all appears correct. However, when doing a Channel.arel_engine.connection.instance_eval {@config[:database]}, I get the standard ActiveRecord database connection returned. It appears as if Arel does not pick up on the modified active_record connection. The funny thing is that this only appears for some of my models. Others return the correct database for both connection and arel_engine.

Another thing to notice is that while Channel.first works fine, I get the following error, when doing a Channel.find(1):

NoMethodError: undefined method `eq' for nil:NilClass

Regards,

Sebastian

Reported by Sebastian Vogelsang · March 1st, 2011 @ 11:46 AM

State: new
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. Sebastian Vogelsang
    Sebastian Vogelsang

    ok, I got it working by changing my code to the following:

    models.each do |klass|
      next unless klass < ActiveRecord::Base
      klass.establish_connection "processing_#{Rails.env}"
      klass.class_eval do
        @arel_engine = klass
        @arel_table = Arel::Table.new(klass.table_name, klass)
        @relation = ActiveRecord::Relation.new(klass, @arel_table)
      end
    end
    

    I hope this is not the way we have to do it from now?!? Shouldn't establish_connection take care of setting up the AREL Relations appropriately?

    Sebastian

    March 1st, 2011 @ 06:21 PM

  2. atomgas
    atomgas

    HasAndBelongsToMany did not work with multiple database, this fixed it for me:

    module ActiveRecord
      # = Active Record Has And Belongs To Many Association
      module Associations
        class HasAndBelongsToManyAssociation < AssociationCollection
          protected
            def insert_record(record, force = true, validate = true)
              if record.new_record?
                if force
                  record.save!
                else
                  return false unless record.save(:validate => validate)
                end
              end
    
              if @reflection.options[:insert_sql]
                @owner.connection.insert(interpolate_and_sanitize_sql(@reflection.options[:insert_sql], record))
              else
                # IMPORTANT!!!
                # here active record does not pass the engine to Arel::Table and thus Arel::Table uses Table.engine = ActiveRecord::Base
                # IMPORTANT!!!
                relation   = Arel::Table.new(@reflection.options[:join_table], @owner.class.arel_engine)
                timestamps = record_timestamp_columns(record)
                timezone   = record.send(:current_time_from_proper_timezone) if timestamps.any?
    
    
                # debugger
                attributes = Hash[columns.map do |column|
                                    name = column.name
                                    value = case name.to_s
                                    when @reflection.primary_key_name.to_s
                                      @owner.id
                                    when @reflection.association_foreign_key.to_s
                                      record.id
                                    when *timestamps
                                      timezone
                                    else
                                      @owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
                                    end
    
                                    [relation[name], value] unless value.nil?
                end]
                relation.insert(attributes)
              end
    
              return true
            end
            
            def delete_records(records)
              if sql = @reflection.options[:delete_sql]
                records.each { |record| @owner.connection.delete(interpolate_and_sanitize_sql(sql, record)) }
              else
                # IMPORTANT!!!
                # here active record does not pass the engine to Arel::Table and thus Arel::Table uses Table.engine = ActiveRecord::Base
                # IMPORTANT!!!            
                relation = Arel::Table.new(@reflection.options[:join_table], @owner.class.arel_engine)
                relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
                  and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
                ).delete
              end
            end
                    
        end
      end
    end
    

    March 22nd, 2011 @ 11:20 AM

  3. Sebastian Vogelsang
    Sebastian Vogelsang

    anyone? This is a rather serious issue! I can't imagine that nobody else has encountered these problems when connecting to multiple databases?!

    April 12th, 2011 @ 11:52 AM

  4. Ibrahim
    Ibrahim

    You don't need to do this in the environment.rb If you want separate connections in your models, use the ActiveRecord::Base.establish_connection method that takes a options hash. Place your database configuration in the appropriate config file and use that in your model:

    class User < ActiveRecord::Base
      establish_connection(
        :adapter => "mysql"
        :host    => "localhost"
      )
    end
    

    If you want to use your YAML, it is available as a symbol within ActiveRecord::Base like so:

    class User < ActiveRecord::Base
      establish_connection :from_yaml
    end
    

    And to keep things dry, you can setup a parent model that establishes the connection to the other database and let your models inherit from it, since establish_connection stays in the family!

    class Person < ActiveRecord::Base
      establish_connection :private_government_database
    end
    
    class User < Person
    end
    
    class Admin < User
    end
    

    Hope this helps you ;-)

    April 13th, 2011 @ 12:55 PM