'When using Postgres Enum types with Rails, specifying CREATE TYPE xyz_setting AS ENUM in your migration breaks your db/schema.rb file

this is based on some dev.to articles here https://dev.to/diegocasmo/using-postgres-enum-type-in-rails-30mo and here https://dev.to/amplifr/postgres-enums-with-rails-4ld0 and the same guy's article here https://medium.com/@diegocasmo/using-postgres-enum-type-in-rails-799db99117ff

If you follow the advice above, you are advised to create a Rails schema migration for your Postgres backed schema by using CREATE TYPE xyz_setting AS ENUM directly on Postgres, and then use that to create your new field as an ENUM (a postgres enum)

Unfortunately, this approach has the downside of breaking the db/schema.rb file.

I think the problem is that the native Postgres types are not supported by the Rails core.

I can reproduce the behavior on Rails 5.17, 5.2.2.4, and 6.0.3

if I do...

class AddXyzToUsers < ActiveRecord::Migration[5.2]
  def up
    execute <<-DDL
          CREATE TYPE xyz_setting AS ENUM (
            'apple', 'bananna', 'cherry'
          );
    DDL
    add_column :users, :xyz, :xyz_setting
  end

  def down
    remove_column  :users, :xyz
    execute "DROP type xyz_setting;"
  end
end

then my schema file is messed up, specifically, the schema users table doesn't get output whatseover, and instead in its place is this message

Could not dump table "users" because of following StandardError

Unknown type 'xyz_setting' for column 'xyz'



Solution 1:[1]

Rails doesn't support custom PG types out of the box.

As stated by Diego Castillo you can use a sql schema which will handle those types.

However, if you want to keep using a ruby schema there's activerecord-postgres_enum, a gem that provides support for PostgreSQL enum data types.

Solution 2:[2]

For Rails 7 Postgres enum support is now built-in

I like @crysicia's answer a lot but to be more precise for the next developer, for Rails 6 you have the choice of either:

  1. using activerecord-postgres_enum
  2. SWITCHING your Rails app to SQL schema (assuming you are using the native Rails schema at db/schema.rb) which instead produces db/structure.sql and will write your schema in native SQL of course.

The #2 approach is frowned upon by the Rails-DB separation purists, of course, but as-is this entire implementation anyway since I don't know of other Dbs that support the enum anyway (there may be).

So basically if you want to keep db/schema.rb I'd recommend using the activerecord-postgres_enum gem

Solution 3:[3]

In Rails 7 if you are using PostgreSQL you can now do:

rails generate model Post title:string body:text post_type:enum

Your migration can be like:

class CreatePosts < ActiveRecord::Migration[7.0]
  def change
    create_enum :post_option, ["draft", "private", "published"]
    
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.enum :post_type, enum_type: "post_option", default: "draft", null: false

      t.timestamps
    end
    add_index :posts, :post_type
  end
end

Your schema would look like:

ActiveRecord::Schema[7.0].define(version: 2022_06_04_221707) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  # Custom types defined in this database.
  # Note that some types may not work with other database engines. Be careful if changing database.
  create_enum "post_option", ["draft", "private", "published"]

  create_table "posts", force: :cascade do |t|
    t.string "title", default: ""
    t.text "body", default: ""
    t.enum "post_type", default: "draft", null: false, enum_type: "post_option"
    t.string "image", default: ""
    t.index ["post_type"], name: "index_posts_on_post_type"
  end
end

Then on your model you would simply put:

enum :post_type, draft: "draft", private: "private", published: "published"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Crysicia
Solution 2
Solution 3 drjorgepolanco