Reputation: 3264
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
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
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
Reputation: 493
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.
7-1-stable
branchUsing 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
class AddMissingEnumsColumns < ActiveRecord::Migration[7.1]
def change
add_column :companies, :auth_type, :string
end
end
Upvotes: 3
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