Ramon Marques
Ramon Marques

Reputation: 3264

How can I fix "Unknown enum attribute" after Rails 7.1 upgrade?

Hi I have this Company model with the following enum that works fine for rails < 7.1

class Company < ApplicationRecord

  enum auth_type: {
    password: 'password',
    magic_link: 'magic_link',
    google: 'google',
    microsoft: 'microsoft',
    saml: 'saml',
    workos: 'workos',
    developer: 'developer'
  }, _prefix: :auth_by

end

Once I try to upgrade to rails 7.1 it stops working because it breaks on the migrations already on CI, with the following error:

Unknown enum attribute 'auth_type' for Company
/home/runner/work/clearyapp/clearyapp/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.0/lib/active_record/enum.rb:174:in `block in load_schema!'

I saw this error raising was added in 7.1 here https://my.diffend.io/gems/activerecord/7.0.8/7.1.0/page/26#d2h-285143-749

The thing I don't understand is how I should fix it if the error doesn't tell me anything useful. It is a known enum attribute =/

Does anyone have an idea of how to fix it?

Upvotes: 12

Views: 3362

Answers (4)

odanetpro
odanetpro

Reputation: 1

In the old migrations, in addition to changing the database structure, there were queries that edited data using the ActiveRecord ORM. For example:

class AddSalaryToEmployees < ActiveRecord::Migration[6.0]
  def change
    add_column :employees, :salary, :float, default: 5000

    Employee.where(first_name: ['Bob', 'Alice', 'Eve']).update(salary: 20000)
  end
end

The same error occurred because the enum status is defined in the Employee model, but the required column does not exist in the database at the time the migration is executed (the column is added in a later migration).

To avoid adding an empty model to the migration as suggested by akaspick (which would cause joins and other model functionalities that may be needed for data modification to stop working), the missing attributes can be added to the parent class of the models - ApplicationRecord. This way, no changes need to be made to the migration."

class ApplicationRecord < ActiveRecord::Base
  def self.inherited(subclass)
    super

    case subclass.name
    when 'Employee'
      subclass.attribute :status, :integer
    when 'OtherModel'
      subclass.attribute :other_enum, :string
    end
  end
end

Upvotes: 0

akaspick
akaspick

Reputation: 1697

If you're running into this issue in migrations due to referencing the model in a previous migration, you can redeclare the enum as an attribute in the migration that is causing problems in order to let it run. Your database backed declaration of the enum can remain as-is in app/models/company.rb

class DoSomething < ActiveRecord::Migration[7.1]
  class Company < ApplicationRecord
    attribute :auth_type, :string
  end

  def change
    ...
  end
end

Upvotes: 2

Paul Bob
Paul Bob

Reputation: 493

For folks that have enums that are not backed by a DB column

It seems that rails 7.1 added validation on enums and no longer supports enums without being backed to a DB column. Check this issue for more details.

@jonathanhefner made this PR that adds support for enums that are non DB backed attributes. The PR was

Backported to 7-1-stable in 3165b6f.

https://github.com/rails/rails/pull/49769#issuecomment-1777833124

I see 2 possible approaches on this issue.

Use the 7-1-stable branch

Using the 7-1-stable branch and adding an attribute for the enum should remove the error.

gem 'rails', git: 'https://github.com/rails/rails.git', branch: '7-1-stable'
class Company < ApplicationRecord

  attribute :auth_type, :string
  enum auth_type: {
    password: 'password',
    magic_link: 'magic_link',
    google: 'google',
    microsoft: 'microsoft',
    saml: 'saml',
    workos: 'workos',
    developer: 'developer'
  }, _prefix: :auth_by

end

Add DB column for that enum

class AddMissingEnumsColumns < ActiveRecord::Migration[7.1]
  def change
    add_column :companies, :auth_type, :string
  end
end

Upvotes: 3

Ramon Marques
Ramon Marques

Reputation: 3264

It ended up being what les-nightingill suggested on his comment to the question.

I had a Company.where().find_each logic inside an earlier migration file that was before the migration that added the auth_type column. Hence, the model was trying to initialize the enum types before the column existed.

The fix was to remove the Company usage inside of the migration, in my case, because I didn't need that anymore. If you run into this same issue you'd probably need to find a way to do whatever you need without using the model directly, which will enforce the enum logic.

Upvotes: 0

Related Questions