外部キー制約って何ですか?
削除機能追加時に出るエラー「foreign key」とは
Micropostを作るときに出てきた「外部キー制約」について。
説明がわかりにくかったので、具体例を挙げてメモしておくことにしました。
外部キー制約(foreign_key)とは
「そのidを持つレコード(=dbでいう横列)が、参照先のテーブルに存在することを保証する制約」のこと。
具体例
4行目。5行目にforeign_key:trueとありますが、これが「外部キー制約」です。
4行目のコードのみに絞って話を進めると、
作られたfavoritesテーブルには、 "user_id" というuserテーブルのidレコードへの外部キー制約がつけられています。
つまりfavoritesテーブルにuser_id = 1というレコードがある場合、userテーブルにもid = 1のレコードが存在しなければならないという制約がつけられているということです。
(図表を用意)
削除機能追加時の注意点
「外部キー制約」を持たせている場合には、dependent: :destroy(依存して一緒にdestroy)の追記が必要です。
例えばtwitterクローンの場合。
ユーザ退会機能を実装する際、userテーブルのid = 1のレコード(のみ)を消すと、favoritesテーブルのuser_id = 1というレコードから見ると「あれ?user_id = 1のuserがいないけど」となってしまい、エラーが発生します(=外部キー制約の違反になってしまう。)
なので、userテーブルのid = 1のレコードを消すときには、favoritesテーブルのuser_id = 1も依存して(=一緒)に消してあげるよう設定する必要がある。
それがdependent: :destroy
今回ならuserテーブルに依存しているのがfavoritesテーブルなので、
has_many :favoritesの後ろにdependent: : destroyを書けば良い。
16〜18行目。
すると、ユーザー情報を削除すると、そのユーザーがお気に入り登録していたpost_id等も一緒に消去してくれます。
herokuでエラー
原因が掴めない!本番環境のエラー!(heroku編)
環境変数などの設定をせずにherokuアプリ開こうとしたらこんなものが。
「申し訳ないけど、なんか変だ」つまりエラーってことですね。
これでは原因が全く掴めないのですが、私はど素人ミスでこの表示が出てきてしまったので、備忘録として書いておくことにします。
パターン①ただのマイグレーション忘れ
とりあえずマイグレーションします。
$ heroku run rails db:migrate
そしたらできました。
パターン②admin(=管理者設定)したらエラー表示がでた
production環境では表示されていたページがなぜかherokuでは表示できなくなってしまった。logでエラー原因を探ると、
ActionView::Template::Error (undefined method `admin?' for #<User:0x00005563b67fb948>):
admin?なんて定義してないのにコードで出てるよ!
とお叱りを受けました。
要は、heroku上のデータベースと開発環境のコードが一致していないためエラーが起きているようです。
ということで、
$heroku run rake db:migrate
$heroku restart
で再設定をしたら無事表示できました。
どちらも素人的ミスでした。
Herokuのアプリを開けない
開こうとします。
https://(自分のHerokuのアプリ名).heroku.com/
すると、
No such file or directory(そんなファイルもディレクトリ(=フォルダ)も無い)
とお叱りを受けました。これは環境変数の設定を行なっていないことが原因のようです。
設定しましょう。
次にheroku runコマンドでHerokuアプリ上でのコマンドを実行する。
$ heroku run rails db:migrate
するとできました。
マイグレーション:移行、移動などの意味を持つ英単語。 ITの分野では、ソフトウェアやシステム、データなどを別の環境に移転したり、新しい環境に切り替えたりすることを意味する
管理者権限の機能を追加したい!
usersテーブルにadmin(=管理人)カラムを追加することで管理者権限の機能を追加してみようと思います。参考サイトはこちらから。
機能追加にあたり、前提として以下のものを作成しております。
・usersテーブルの作成
usersテーブルにadminカラムを追加(データ型はboolean)
$rails g(=generate) migration add_admin_to_users admin:boolean
すると[db/migrate(作成日)_add_admin_to_users.rb]にこのようなファイルが。
class AddAdminToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :admin, :boolean
end
end
このままでも良いのだが、自分で以下の部分を追記します。
class AddAdminToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :admin, :boolean, default: false
end
end
default: falseは「default(=初期値)では管理者になれない」ということを明示しています。本来記述不要だが、書いた方がわかりやすいという理由で書くそうです。
マイグレーションの実行
$rails db:migrate
管理者データを作成[db/seeds.rb(サンプルデータ生成タスク)]で管理者を1人追加
※赤字部分は自分で決める
User.create!(name: "admin",
email: "admin@admintest.com",
password: "admintest",
password_confirmation: "admintest",
admin: true)
ラストのadmin: trueが管理者であることを明示しています。
そして再度マイグレーションの実行
$rails db:migrate
これで管理者用ユーザができた。
使用例
管理者としてログインしたときにしか表示されない部分を作ってみました。
テーブルのカラムタイトルの部分ですが、
<% if current_user.admin? %>
<th class="text-center">ユーザ削除</th>
<% end %>
もしログインユーザーがadmin=trueならユーザ削除のカラムも表示
となっています。
これはできると作成物の幅が広がります。
Herokuのデータベースをリセットしたい!
一度作成したデータを全てリセットしたい!
面倒な手順を踏まず簡単にHerokuに登録したデータをリセット(全削除)したい!ということでやってみました。
①Herokuのデータベースをリセットするコマンド
$heroku pg:reset DATABASE
するとこのような表示が。
▸ WARNING: Destructive action
▸ ○○ will lose all of its data
▸ To proceed, type xxxxx or re-run this command with xxxxx
【ざっくり日本語訳】
▸ 警告:削除のアクションです
▸ ○○のデータが全て失われますよ
▸ (削除を)進めるにはxxxxxをコマンド入力してください
②xxxxxをコマンド入力
指示に従い、xxxxxに表示されたものを入力。すると以下の表示が。
Resetting ○○... done
【ざっくり日本語訳】
○○をリセットしています...終わりました。
③再度テーブルをセットする
その後再度マイグレーションしてテーブルをセットしなおす
$ heroku run rails db:migrate
これで無事できました。
「年月日」の形式で投稿日を表示させたい!
webアプリケーションで投稿した日時を表示させたく、以下のようにコードを書いた。
17行目に<div>投稿日:<%= post.created_at %></div>と記述した。
すると以下のように。
投稿日は表示できたものの「UTC」とかいらんし…もっと綺麗に表示させたい。
ってことで調べると以下のサイトに記述方法が書いてあった。
上記サイトを参考に以下のように記述を変更した。
<div>投稿日:<%= post.created_at.strftime("%Y年%m月%d日") %></div>
すると思っていたように表示できました。
link_toが機能しない!
posts#show(PostsControllerのshowアクション)からview/posts/show.html.erbを表示させたい。
ということでまず、rails routeのコマンドでリンク先のurlを取得
左列のPrefix(○○_pathと入力することでリンク先へ飛べるメソッド)を使うと、「posts#show(PostsControllerのshowアクション)からview/posts/show.html.erbを表示させるには、"post"を使え」と書いてある。つまり、"post_path(インスタンス変数)"いうメソッドを使えばリンクを飛ばせるよ、ってことらしい。
そこで12行目に以下のメソッドを入力。
するとエラーが発生する。
ここは本題からそれるので概要だけ話すと、(ルーティング設定時に自動で生成されるリンクメソッドである)○○_pathを使ってリンク先を書く時に、定義をしていない@post(コントローラーで作る変数)を使うこと自体おかしいらしい。
なので今度は8行目に記載した<%= @posts.each do |post| %>で全てのポスト(全投稿データ)をそれぞれ1つずつ表示するメソッドを記入しているので、そのそれぞれ1つずつの意味を表す変数postをリンク先の変数として入れた。
(12行目のコードがpost_path(@post)からpost_path(post)にしたということ)
でもエラー。
なぜか。
それは、私がリンク先を表示するために作ったPostsControllerのshowアクションに原因があったのだ。
エラーコードとして出ているが、postsテーブルからid:13の投稿データを取得したいのに、@user = User.find[params(:id)]も記述したことにより、usersテーブルからid:13のユーザ情報も取得しようとしてしまったことで「おい、13人目のユーザ(id:13を持つユーザ)なんて見つけられねーぞ!」とお叱りを受けてしまったのだ。
ということで、この1文(@user = User.find[params(:id)])を削除。
を
にしました。
が、また再チャレンジするとエラー。
原因はposts/show.html.erbの5行目に記述したrender内の変数を設定するところ。
(user: @userの部分)
今度は@user変数を削除したことにより、1行目に記述した
<%= render "users/users", user: @user %>が機能しなくなってしまった。
おそらく過去の僕は
- posts#show内で@user変数を用意
- render内でuser変数に代入
- 現ユーザの情報を取得して表示
させようと思ったのだろう。
でも、@userを使うとエラーが出るので、それは出来ないということらしい。
では何をrenderのuser変数に入れれば良いのか...
写真を投稿したuserデータを表示するには…
悩むこと1時間...
ん?
写真を投稿したuser情報...あ!
ピキーン
こいつを入れればもしかして...(5行目を@user→@post.userに変更)
無事表示できました。よかった。
【番外編(ちょっと愚痴)】
今回のエラーは結構苦戦したので、私は序盤で現役エンジニアの方(メンター)に相談したのですが、最初の相談で「このコードでは表示できませんね〜、ここを見てください」と解説リンク貼り付けで一蹴されました。
ど素人から言わせてもらえば、自分で調べても解決しなかったから他人(メンター)に助けを求めたのに、それを「もっと自分で調べろ!(=自分でなんとかしろ!)」っていうのはどうなんだろうと思いました。いやまぁ自己解決能力を上げるのも大切ですが、こちらはお金を払って聞いている訳なので、その対応はちょっとね…と若干怒りモード入りました。
メンター選びは大切ですね。