Custom Database Type in ActiveRecord

2019-02-12 14:47发布

问题:

I am using Rails 3.1.1 with PostgreSQL 9.1 and the earthdistance module. To be able to calculate the distance between different locations properly, I have setup a column with the earth type in my branches table.

The problem I am experiencing now is that my Rails application that uses this table does not understand the earth type and thus I am getting this in my db/schema.rb:

# Could not dump table "branches" because of following StandardError
#   Unknown type 'earth' for column 'location'

This is problematic since now I can't create my test database from the schema.rb.

How can I add this type to AR or make it ignore that column?

回答1:

Try this:

Change you config/application.rb

config.active_record.schema_format = :sql

This will change the output format to the native PostgreSQL SQL format, schema.rb will be disabled and a new file will be generated /db/config/structure.sql



回答2:

Another case where someone might run into this is when inserting your custom postgres types (enums) into postgresql. If one does this and still wants to use Ruby for your schema.rb, you can add custom database types to the adapter's list of valid types.

For example, let's say you have a migration like so, with a address_type and address_status.

class CreateAddresses < ActiveRecord::Migration
  def up

    execute <<-SQL
      CREATE TYPE address_status AS ENUM ('active', 'archived');
      CREATE TYPE address_type AS ENUM ('billing', 'shipping');
    SQL

    create_table :customer_addresses do |t|
      # bla bla
      t.column :address_type, :address_type
      t.column :status, :address_status

      t.timestamps null: false
    end
  end

  def down
    drop_table :customer_addresses
    execute <<-SQL
      DROP TYPE address_type;
      DROP TYPE address_status;
    SQL
  end

end

Then in create an initializer or add something like this to your application.rb:

# config/initializers/postres_enum_custom_types.rb
module ActiveRecord
  module ConnectionAdapters
    if const_defined?(:PostgreSQLAdapter)
      class PostgreSQLAdapter
        NATIVE_DATABASE_TYPES.merge!(
          address_status:  { name: 'character varying' },
          address_type:    { name: 'character varying' }
        )
      end
    end
  end
end

Notes about my solution:

  • I am checking the existence of the PostgreSQLAdpater because of the way a static analysis gem I'm using partially loads some AR dependencies - specifically the gem annotate.
  • The source for the original definition is here.

Background on my running into this: When this happened to me in rails 5.0.x, the migrations ran fine, but then my test environment would fail when trying to run db:test:prepare or db:reset. It took me quite a while to track this down to the schema dump issue.