rails: Rails 7.1 - can't define enums for attributes which don't exist in table

Not sure if this is an intended change or not. Pre 7.1, I could define an attr on a model using attribute and define enum values for it, then use enum helper methods. I’m using this heavily in my current project for i18ns etc, so defining the enum values as an array and manually validating won’t cover all my use cases

Since 7.1, this isn’t possible.

Steps to reproduce

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails", branch: "main"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
  end
end

class Post < ActiveRecord::Base
  attribute :topic
  enum topic: %i[sports politics entertainment]
end

class BugTest < Minitest::Test
  def test_association_stuff
    post = Post.create!
    post.assign_attributes(topic: :sports)

    assert_equal true, post.sports?
  end
end# frozen_string_literal: true


Expected behavior

Post should instantiate correctly and can use enum helper methods

Actual behavior

Error thrown

RuntimeError: Unknown enum attribute 'topic' for Post

System configuration

Rails version: 7.1.1

Ruby version: ruby 3.1.2p20

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Comments: 22 (12 by maintainers)

Commits related to this issue

Most upvoted comments

I am facing this same issue when the model is used in some migration before the enum is introduced for example

# app/models/user.rb
class User < ApplicationEnhancedVersionedRecord
  enum status: { draft: 'draft', published: 'published' }, _default: :draft
end

# 20220101000000_add_posts
class AddPosts < ActiveRecord::Migration[7.0]
  def change
    create_table :posts, id: :uuid do |t|
      t.string   :title, null: false
    end
  end
end

# 20220102000000_some_data_migration
class AddStatusToPosts < ActiveRecord::Migration[7.0]
  def change
    Post.find_each {...}
  end
end

# 20220103000000_add_status_to_posts
class AddStatusToPosts < ActiveRecord::Migration[7.0]
  def change
    add_column :posts, :status, :string, default: "draft"
  end
end

Now I get

StandardError: An error has occurred, this and all later migrations canceled: (StandardError)

Unknown enum attribute 'status' for Post

Yes, that was intentional. See related issue https://github.com/rails/rails/issues/45668 for the discussion of the problem.

cc @byroot

I have issue similar to the one @jasonl has. There is an 3 years old migration in my app, which adds a column to User database table, and it populates it in the loop for each user:

def up
    add_column :users, :new_filled_column, :string

    User.find_each do |user|
      user.update(new_filled_column: User.default_field_value)
    end
  end

A year later another migration adds new column for storing enum value

add_column :users, :another_enum_column, :integer

and corresponding code to set its values

enum another_enum_column: { value_one: 1, value_two: 2 }, _prefix: true

So now when I reset my schema, I get an error: “Unknown enum attribute 'another_enum_column' for User”, because column another_enum_column doesn’t yet exist when older migration calls User model.

@byroot Is it possible you might have some kind of solution to resolve this error? I really don’t like the idea to edit old migrations.

We have also encountered this issue, but in a slightly different way. We have a model where we added an enum column to it in a migration created long after the original table. This now creates the problem where we cannot run our migrations for an empty database, as the model defines the enum, but the column backing it doesn’t exist in the DB, and won’t until the migration is run which creates it.

We’ve hacked around it by defining an empty AR model for the table in the migrations which break, but this seems inelegant to say the least.

In several of my project’s models, we define enum’s whose values are used to populate a character varying column (we are using Postgres). An example enum is below:

    enum order_by_types: {
      email: 'email',
      fax: 'fax',
      phone: 'phone',
      website: 'website'
    }

Before Rails 7.1, referencing and using enum’s like order_by_types above did not raise any errors. With Rails 7.1, tests are now failing with errors like the following:

     RuntimeError:
       Unknown enum attribute 'order_by_types' for Deals::Judgment

What I am looking for is some guidance on how best to fix this. It appears that we can no longer define enum’s inside models without a corresponding database table column.