Model
Modelクラス
「モデル」でデータベースから情報を取り出す。
あるデータベースから取り出せるモデルは限定されており、命名にも強い関連がある。
コントローラでデータのやり取りをするために必要で、通常はコントローラー作成後にモデルも作成する。
種類 | 命名規則 | 例 |
モデルクラス名 | 先頭は大文字、単数形 | Product |
モデルクラスのファイル名 | 先頭は小文字、単数形 | product.rb |
テーブル名 | 先頭は小文字、複数形 | products |
以下のようにしてモデルを作成する。命名規則からhogehogeは全て小文字
rails g model hogehoge
migrationファイル
データベースを作成するための設計図のようなもの。
前述のmodelをgenerateした際に同時に作成されている。
class CreateTweets < ActiveRecord::Migration[5.2]
def change
create_table :tweets do |t|
t.string :name
t.text :text
t.text :image
t.timestamps null: true
end
end
end
t.string ~ :imageまでは自分で作成。
テーブルに必要な型とカラム名を指定する。
t.型 :カラム名と記述。
このままだと設計図止まりなのでテーブルを作成する。
rake db:migrate
rails generate migrationコマンド
テーブルにカラムを追加したり、削除するようなテーブルの構造を変えたい時もマイグレーションファイルを作成する。
rails generate migrationコマンドで新たなマイグレーションファイルを作成した後、
rake db:migrateでカラムを追加/削除する。
bundle exec rails g migration AddRateToProducts
#migrationファイルを編集
rake db:migrate
テーブルへカラムを追加 add_column
マイグレーションファイルのchangeメソッド内に書いてカラムを追加する。
add_column :テーブル名, :カラム名, :カラムの型
なおmigrationファイルを作成する際に指定することもできる。
rails g migration AddIntroductionToUsers introduction:text
Addカラム名Toテーブル名 追加するカラム:型
Addの後のカラム名に関してはなんでもいい。
AddColumnToUser とか。見てわかりやすい名称が望ましいだけなので好みで。
テーブルからカラムを削除
migrationファイルを作成
rails g migration Removeカラム名From削除元テーブル名 削除するカラム名:型
rakeする
rake db:migrate
データの型の変更
rails g migration change_datatype_変更したいカラム名_テーブル名
この表記をよく見るが、結局できるのは空のmigrationファイル。
class ChangeDatatypeUserIdPosts < ActiveRecord::Migration[5.2]
def change
change_column :posts, :user_id, :integer
end
end
change_colomnでテーブル名、カラム名、型を指定することで変更する。
migrationファイルができたらrake db:migrateして終了
属性の貼り方
index
add_index / remove_index
migrationファイルに書き込む場合は以下
class CreateGroups < ActiveRecord::Migration[5.0]
def change
create_table :groups do |t|
t.string :name, null: false
t.index :name, unique: true
end
end
end
unique: trueは必要ないですが、一意制約を掛けたい場合に使ってください。
t.string :name, index: trueとも書けるようですが、あまり書かれてる形跡がないので使わない方がよさそう。
後からindexを張る場合にはこちら。
def change
add_index :products, :title
end
コマンドで一気に作成するには以下
rails g migration AddTitleToProducts title:sting:index
外部キー制約
多くの場合IDを外部キーとして用いるが、integerではなくreferences型と宣言する。
その上でforeign_key: trueにする。
Add_Columnのときはreferenceと単数形
class AddIntroductionToMembers < ActiveRecord::Migration[5.0]
def change
add_reference :members, :user, foreign_key: true
add_reference :members, :group, foreign_key: true
end
end
新規にmigrationファイルに書き込んで定義する時は複数形
(こんなん分かるわけないだろ)
ちなみにわざわざuser_idと書く必要はなく、テーブル名の記載にすれば、自動的にuser_idのカラムが作成される。
t.references :user, foreign_key: true
外部キーに設定すると、mySQL上ではMUL(multiple key)という名称になる。
カラムの足し引きと外部キー
足す場合
rails g migration AddColumnToTable
migrationファイルを作成して、以下を書き込む
class AddIntroductionToMembers < ActiveRecord::Migration[5.0]
def change
add_reference :members, :user, foreign_key: true
add_reference :members, :group, foreign_key: true
end
end
rails db:migrateして終了
引く場合
rails g migration RemoveUser_idFromMembers
Remove_Fromでmigrationファイルを作成。
def change
remove_column :users, :name
end
アソシエーション
モデルクラスにhas_manyやbelongs_toなどの定義する。
class Hogehoge < ApplicationRecord
belongs_to :user(単数形)
end
class User < ApplicationRecord
has_many :hogehoges(複数形)
end
このように定義するとuserに所属するhogehogeが簡単に呼び出せる。
アソシエーションなし。
user = User.find(1)
Hogehoge.where(user_id: user.id)
アソシエーションあり。
user = User.find(1)
user.hogehoges
Userのインスタンスから所属するhogehogeを引くことができる。
逆も然りで、HogehogeのインスタンスからUserを引くこともできる。
hogehoge.userでユーザーのインスタンスを取得できる。
has_many throughオプション
through: :photos_tags 両方ともシンボル
多対多を解消する際に用いる。
顧客ー注文ー商品のようなテーブル構造にした際、連携するテーブルと中間テーブルをhas_manyで連携し、なおかつ中継テーブルはthroughオプションを使う。
中間テーブルは慣例として、名称を連携テーブル名1_連携テーブル名2にする。
# app/models/photo.rb
class Photo < ActiveRecord::Base
has_many :photos_tags
has_many :tags, through: :photos_tags
end
# app/models/tag.rb
class Tag < ActiveRecord::Base
has_many :photos_tags
has_many :photos, through: :photos_tags
end
# app/models/photos_tag.rb
class PhotosTag < ActiveRecord::Base
belongs_to :photo
belongs_to :tag
end
オプション名 | 用途 |
---|---|
class_name | 関連するモデルのクラス名を指定でき、関連名(photos_tags等、has_manyの直後に書くもの)と参照先のクラス名(PhotosTagのようなモデル名)を異なるものにできる |
foreign_key | 参照先を参照する外部キーの名前を指定できる(デフォルトは、参照先のモデル名_id) |
dependent | 親モデルのデータを消したら関連するモデルのデータも連動して消したいときに使用します。destroyとdelete_allでひとつひとつ消していくか、一気に消すかを指定できる |
source | 関連テーブルから先のモデルにアクセスするための(関連モデルから見た)関連名を指定できる |
中間テーブル
userとgroupで中間テーブルを組む場合、単数形で命名する。
rails g model user_group
この時、テーブル名はuser_groupsとなったり、
has_many :users, through: :user_groups
となったり、単数複数が入り混じるが仕方がない。
気になる場合は中間テーブルを別名にしたりで対処する。
dependent: :destroy
親が削除された際の挙動を決める。
destroyの場合は削除。
has_many :shippings, dependent: :destroy
validation
モデルの作成後はvalidationを追加する。
class Group < ApplicationRecord
has_many :members
validates :name, presence: true
end
rakeコマンド
rake db:reset
全てのテーブルをドロップして、schemaを元にmigrateする。
rake db:migrate:reset
全てのテーブルをドロップして、migrationファイルを元にmigrateする。
rake db:rollback
1つ前のmigration状態へ戻る。
rake db:version
現在のmigrationの位置を見れる。
validation (検証)
入力フォームを通じてビューからサーバー側へパラメーターが送られてきた際、正常な値か検証する。未入力を撥ねるために使う。
validates :カラム名, presence: true
フォームの中身の有無を検出してくれる。ない場合には保存処理をしない。
例えば、userのemailを入力必須にしたい場合、以下のように書く。
user.rb
class User < ApplicationRecord
validates :email, presence: true
ユーザーの新規登録時にemailを入力しなかった場合、userを登録することができなくなる。
Validator
モデルの役割の一つにバリデーションがある。
バリデーションとはデータの整合性を保つために、データを検証する機能のこと。
あるモデルのバリデーションに複雑な処理があったり、複数のモデルに共通のバリデーションが存在する場合にはそれらをモデルから切り離すことでリファクタリングが可能。
app/models/articles.rb
class Article < ActiveRecord::Base validates :name, url_format: true end
app/validators/url_format_validator.rb
class UrlFormatValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.present? && value !~ /\A#{URI::regexp(%w(http https))}\z/ record.errors[attribute] << "のフォーマットが不正です" end end end
validatorsフォルダ内のファイル名から_validatorを取り除いたものを各クラスのvalidatesメソッドに引数として渡すと、そのカラムを検証する際にファイル内のメソッド(validate_eachメソッド)が実行される。
カスタムバリデーション
app/validatorsフォルダを作成し、配下にvalidationファイルを設置する。
user_validator.rb
class UserValidator < ActiveModel::Validator
def validate(record)
items = [record.nickname, record.email, record.birthday]
return if items.any?(:present?)
record.errors[:base] << '入力漏れがあります'
end
end
定義したvalidatorを使うにはvalidate_withで呼び出す。
model user.rb
class User < ApplicationRecord
validates_with UserValidator
end
valid?
条件分岐する場合にはsaveと違い、if createだとcreateに失敗してもtrueとなるため、valid?でバリデーションをかけて評価する。
def create
if User.create(user_information_params).valid?
redirect_to root_path
else
render "phone_confirmation.html.haml"
end
end
エラー
foreign_key bigint
usersテーブルを設定したあと、t.referencesで外部キーを持つテーブルを作成した際にuser_idがbigint(20)になって外部キーが貼れない。(integer型と型が違うから)
def change
create_table :shippings do |t|
t.references :user, foreign_key: true
end
end
外部キーを含まない状態でcreate_tableしたのち、add referenceすれば型違いは吸収される。
class CreateShippings < ActiveRecord::Migration[5.2]
def change
create_table :shippings do |t|
t.integer :zipcode, null: false
end
add_reference :shippings, :user, foreign_key: true, null: false
end
end
migrationの順番で外部キーがないと怒られるのでmodel作成順に注意する。
適当な順でmodelを作る場合は、外部キー以外のテーブルを作成して、別途外部キーを最後にadd_tableする。
コメント