いくつかのモデルの設計をリファクタリングしたい状況に何度か遭遇し、最終的に更新ロジックを移行に入れました。ただし、私が理解している限り、これは適切な方法ではありません (特に、移行ではなく展開にスキーマ ファイルを使用することが推奨されているため)。このような問題にどのように対処しますか?
言いたいことを明確にするために、User モデルがあるとします。ユーザーには「通常の」ユーザーと管理者の 2 種類しかないと思っていたので、ユーザーが管理者かどうかを示す単純なブール値フィールドを使用することにしました。
しかし、しばらくして、モデレーターなどの第 3 の種類のユーザーが必要であると考えました。この場合、UserType モデル (および対応する移行) を追加し、ユーザー テーブルから "admin" フラグを削除するための 2 番目の移行を追加します。そして、ここで問題が発生します。「add_user_type_to_users」移行では、管理フラグの値をユーザー タイプにマップする必要があります。さらに、これを行うには、ユーザー タイプが存在する必要があります。つまり、シード ファイルを使用できず、移行でユーザー タイプを作成する必要があります (これも悪い習慣と見なされます)。状況を表す架空のコードを次に示します。
class CreateUserTypes < ActiveRecord::Migration
def self.up
create_table :user_types do |t|
t.string :name, :nil => false, :unique => true
end
#Create basic types (can not put in seed, because of future migration dependency)
UserType.create!(:name => "BASIC")
UserType.create!(:name => "MODERATOR")
UserType.create!(:name => "ADMINISTRATOR")
end
def self.down
drop_table :user_types
end
end
class AddTypeIdToUsers < ActiveRecord::Migration
def self.up
add_column :users, :type_id, :integer
#Determine type via the admin flag
basic = UserType.find_by_name("BASIC")
admin = UserType.find_by_name("ADMINISTRATOR")
User.all.each {|u| u.update_attribute(:type_id, (u.admin?) ? admin.id : basic.id)}
#Remove the admin flag
remove_column :users, :admin
#Add foreign key
execute "alter table users add constraint fk_user_type_id
foreign key (type_id) references user_types (id)"
end
def self.down
#Re-add the admin flag
add_column :users, :admin, :boolean, :default => false
#Reset the admin flag (this is the problematic update code)
admin = UserType.find_by_name("ADMINISTRATOR")
execute "update users set admin=true where type_id=#{admin.id}"
#Remove foreign key constraint
execute "alter table users drop foreign key fk_user_type_id"
#Drop the type_id column
remove_column :users, :type_id
end
end
ご覧のとおり、問題のある部分が 2 つあります。最初に、すべての移行を連続して実行する場合に必要な最初のモデルの行作成部分、次に「admin」列を「type_id」列にマップする 2 番目の移行の「更新」部分です。
何かアドバイス?