STI with the devise User model

Hello everyone, I’m trying to have multiple models which inherits from the devise User model. I’ve made the following migration files:

# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email, null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :username, null: false, default: ""
      t.string :name
      t.string :surname
      t.string :address
      t.string :phone
      t.string :mobile
      t.string :city
      t.string :region
      t.string :state
      t.string :country
      t.string :avatar
      t.string :type

      ## Recoverable
      t.string :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      # Trackable
      t.integer :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string :current_sign_in_ip
      t.string :last_sign_in_ip
      # Confirmable
      t.string :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      t.timestamps null: false
    end

    add_index :users, :email, unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, :username, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

and

class CreateTechnicians < ActiveRecord::Migration[7.1]
  def change
    create_table :technicians do |t|
      t.float :working_hours
      t.float :lunch_break
      t.float :time_to_get_back
    end
  end
end

the schema.rb then is as follows:

create_table "technicians", force: :cascade do |t|
    t.float "working_hours"
    t.float "lunch_break"
    t.float "time_to_get_back"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "username", default: "", null: false
    t.string "name"
    t.string "surname"
    t.string "address"
    t.string "phone"
    t.string "mobile"
    t.string "city"
    t.string "region"
    t.string "state"
    t.string "country"
    t.string "avatar"
    t.string "type"
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string "current_sign_in_ip"
    t.string "last_sign_in_ip"
    t.string "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.string "unconfirmed_email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
    t.index ["username"], name: "index_users_on_username", unique: true
  end

but when I try to do t=Technician.new and then I do t.attributes I only see the attributes defined in the users migration and not the ones defined in the technicians migration. Does anybody have any idea as to why?

Please post app/models/user.rb and app/models/technician.rb.

They are nothing fancy really, just basic devise stuff

user.rb:
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  attr_writer :login
  has_and_belongs_to_many :technicians_couples

  before_save :downcase_email

  devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :validatable, :trackable, authentication_keys: [:login]

  validates :username, presence: true, uniqueness: { case_sensitive: false }
  validate :prohibit_email_as_username

  #select all users but current user
  scope :all_except, ->(user) { where.not(id: user) }

  def login
    @login || self.username || self.email
  end

  def self.find_for_database_authentication(warden_conditions)
    conditions = warden_conditions.dup

    if (login = conditions.delete(:login))
      where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
    elsif conditions.has_key?(:username) || conditions.has_key?(:email)
      where(conditions.to_h).first
    end
  end

  private

  def downcase_email
    self.email = email.downcase
  end

  def prohibit_email_as_username
    if username =~ URI::MailTo::EMAIL_REGEXP
      errors.add(:username, :invalid, message: "can't be an email address")
    end
  end
end
technician.rb
class Technician < User
end

Thanks for posting the models.

You’re assuming that because Technician is a subclass of User, it inherits all of the ActiveRecord attributes. That is not the case. The < operator is a pure Ruby operator that has no effect on the database table definitions.

I suggest you implement a polymorphic relationship between User and an abstract class that we’ll call WorkerType for this discussion. Create a separate migration for each subtype, and the model for each subtype should inherit from WorkerType. (The Rails Guides have a very good write-up about polymorphism.)

Upsides: Clean abstraction and easy two-way navigation between User and WorkerType objects. Downside: If the WorkerType subclasses have a lot of attributes in common, your schema will have a lot of duplications.