2022/10/13(木) Railsのルーティング&アソシエーション
Railsにおけるバリデーションの設定方法
バリデーションとは、正しいデータのみがデータベースに保存されるようにモデルクラスにて、そのデータが正しいかどうかをチェックするシステムのことを言います。バリデーションに引っかかり、データが不正だと判断された場合は、データベースに保存されません。
バリデーションは、正しいデータだけをデータベースに保存するために行われます。正しいデータだけをデータベースに保存するのであれば、モデルレベルでバリデーションを実行するのが最適です。モデルレベルでのバリデーションは、データベースに依存せず、エンドユーザーがバイパスすることもできず、テストもメンテナンスもやりやすいためです。Railsではバリデーションを簡単に利用できるよう、一般に利用可能なビルトインヘルパーが用意されており、独自のバリデーションメソッドも作成できるようになっています。
バリデーションの設定方法
validates :カラム名, presence: true validates :カラム名, length: { maximum: 30 } validates :カラム名, numericality: { greater_than: 0 }
presence: true
は、カラムのデータは、必ず存在する(空のデータでは、保存できないように制限)ように設定したバリデーションです。length: { maximum: 30 }
は、カラムの文字数を最大30文字に制限するバリデーションです。(30文字以上のデータは、DBに保存されない)numericality: { greater_than: 0 }
は、0以上の数値のみの入力を許可するバリデーションです。
numericalityのオプション | 概要 |
---|---|
only_integer | integerのみ |
equal_to | 指定された値と等しいか |
greater_than_or_equal_to | 指定された値以上 |
less_than_or_equal_to | 指定された値以下 |
greater_than | 指定された値よりも大きいか |
less_than | 指定された値よりも小さいか |
odd | trueに設定した場合、奇数か |
even | trueに設定した場合、偶数か |
has_many :through関連付け
has_many :through
関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。 この関連付けでは、2つのモデルの間に「第3のモデル」(joinモデル)が介在し、それによって相手のモデルの「0個以上」のインスタンスとマッチします。
class Restaurant < ApplicationRecord has_many :line_foods, through: :foods end
- 1つのRestaurantは中間テーブルFoodsを経由して複数のLine_foodを持っています。
has_many :through
を使うと、1つのレストランにつき仮注文は、複数作成できますが、その仮注文は、foodテーブルを参照していますよという意味になります。
belongs_toのoptionalオプション
optional: true
とは、アソシエーションによって紐づけられた外部キーの値が存在しない値やnilの場合でも、データベースに保存することができるオプションです。
class LineFood < ApplicationRecord belongs_to :order, optional: true end
- 上記の例で言うと、仮注文テーブルの外部キーにorder_idがセットされていない場合でもバリデーションチェックが行われることなくDBに保存できるようになります。
optional: true
を設定すると、belongs_toの外部キー
のnil
(存在しないこと)を許可します。
インスタンスメソッドとは?
インスタンスメソッドとは、クラスから作成されたオブジェクトに対して、呼び出せるメソッドのことです。 インスタンスメソッドをコントローラーではなく、モデルに記述すると、様々な箇所からオブジェクトに対して呼び出すことができます。
- 特定のデータ(インスタンス)に対する操作は、インスタンスメソッドが使われます。
- 対して、クラスメソッドは、クラス全体に対する共通の操作を行いたい時に使われます。
- クラスメソッドの定義は、
self.メソッド名
のようにメソッド名の前にself
をつける必要があります。
ルーティングの名前空間について
namespace: 名前空間名
で名前空間をつけられます。名前空間をつけることのメリットは、コントローラーを名前空間名のディレクトリで、クループ化するとともに、URLにも名前空間の情報をつけることが出来ることです。
つまり、名前空間というディレクトリを作成することにより、分かりやすくグループ化したルーティングを作成できるようになります。
namespace :a do namespace :b do ... end end
- コントローラーを
app/controllers/a/b/
というディレクトリに分類し、それをURLにも適用するためにnamespaceが使われています。namespaceであるa
とb
が入ったURLでルーティングを作成できます。
ルーティングを作成するresourcesメソッドとresourceメソッド
どちらもルーティングを簡単に作成するためのメソッドです。基本的なアクション(index、show、new、create、edit、update、destroy)に対応するURLの組み合わせ(ルーティング)を作成します。
resorcesメソッド
「resourcesメソッド」は、7つのアクション(index、show、new、create、edit、update、destroy)のidを付与したルーティングを一括生成してくれるメソッドです。
resourceメソッド
「resourceメソッド」は6つのアクション(show、new、create、edit、update、destroy)のルーティングがidなしで生成されます。加えて、「resourceメソッド」はindexアクションのルーティングが生成されません。
resourcesとresourceの使い分け
resources :restaurants do resources :foods, only: %i[index] end
resources
は、リソースが複数ある場合に使用します。ルーティングに付与されるidを使って、複数あるリソースを識別します。- 上記の例で、レストランは、あちらこちらにたくさん存在するので、
resources
を使用しています。
resources :users do resource :profile end
resource
は、リソースが1つしかない時に使用します。リソースが複数ではないため、リソースの一覧ページを表示する必要性がありません。よって、resource
では、indexアクションに対するルーティングが作成されないのです。- 上記の例では、特定のユーザーに対するプロフィール情報は、1つなので
resource
を使用しています。
saveとsave!メソッド
- saveメソッドは、生成したモデルオブジェクトをデータベースに保存します。データベースに保存できなかった場合は、
false
を返します。 - 一方save!メソッドは、同じく生成したモデルオブジェクトをデータベースに保存しますが、保存に失敗した場合は、例外を発生させます。
- 例外を発生すると、例外が起きた場所で処理を停止します。
参考
2022/10/11(火) DB関連のまとめ
RailsにおけるDBとMigrationファイルModelの役割とその関係
- どんなデータベースを作って欲しいかを指示するデータベースの設計図が
Migrationファイル
です。- あるテーブルにこんなカラムが必要、そのカラムに対する型や制約を指示するもので、Migrationファイルを元にDBが作成されます。
- Railsのmodelは、DBのテーブルに対応するRubyのクラスです。 テーブル内のデータがどんなもので、テーブル同士の関係がどうかというアソシエーション関係を定義できます。また、データベースとの間で情報のやり取り(データの取得・保存・検索)を行います。
- モデルおよびモデル内のデータを表現する
- モデル同士の関連付け(アソシエーション)を表現する
- 関連付けられているモデル間の継承階層を表現する
- データをデータベースで永続化する前にバリデーション(検証)を行なう
- データベースをオブジェクト指向スタイルで操作する
- 実際にデータが表のように保管されている箱のようなものが
データベース
です。- データベースとのやりとりを行うのは、モデルです。
RailsプロジェクトをAPIモードで作成する
$ rails new プロジェクト名 --api
APIモードとは?
Rails APIモードとは、API作成に特化したモードのことです。
APIモードではMVCのV(ビュー)が存在しないため、rails newを実行した際にビューに関するファイルやGemが生成されません。
また、通常のRailsアプリケーションではerbファイルをレスポンスとして返しますが、APIモードではJSONをレスポンスに返します。
つまり、Reactなどを使って、非同期的にAPIから外部のデータを取得する場合、JSONデータをレスポンスとして返すRailsのAPIモードを使います。
外部キーの役割
外部キーとはモデル間のリレーションを辿る際の基準となるキーのことです。外部キーを使って、テーブル間のアソシエーション関係を表すことが出来ます。あるテーブルに紐づいたデータを作成したいときに外部キーを使うと自動で紐づいたデータがDBに作成されます。
参考
2022/10/3(月) 基礎から学ぶReact/React Hooks実践入門 Chapter1-1~5
インクリメント演算子とデクリメント演算子
インクリメント演算子は、値を1加算し、デクリメント演算子は、1減算します。 - 前置の場合は、加算・減算後の値を返すのに対し、後置の場合は、計算する前の値が返ります。
演算子 | 演算子名 | 説明 |
---|---|---|
++EX1 | インクリメント(前置) | EX1の値に+1する |
- -EX1 | デクリメント(前置) | EX1の値に-1する |
EX1++ | インクリメント(後置) | EX1の値に+1する |
EX1- - | デクリメント(後置) | EX1の値に-1する |
比較演算子
比較演算子は、左オペランドと右オペランドの大きさを比較し、その結果について真偽値(true/false)で返す演算子です。
厳密等価演算子(===)と厳密不等価演算子(! ==)について
厳密等価演算子と厳密不等価演算子は、どちらも2つのオペランドの型と値を比較します。厳密等価演算子は、2つのオペランドの値が合っていても型が合わない場合は、trueが返りません。
3 === 3 //=> true 3 === "3" //=> false
しかし、等価演算子(==)を使うと、型が異なる場合でも同じ値であれば、自動的に暗黙の型変換が行われ、以下のように文字列の3と数値の3は同じとみなされてしまいます。
3 == 3 //=> true 3 == "3" //=> true
厳密等価演算子と厳密不等価演算子を使うメリットは、暗黙的な型変換を防ぎ、型の安全性を保証してくれるという部分です。
等価演算子(==)を利用するケース
「nullまたは、undefined」を一度に判定したい場合は、例外的に等価演算子(==)が使われます。これは、厳密等価演算子がnullとundefinedを比較するとfalseと評価されるのに対し、等価演算子と使うとtrueと評価されるからです。
null === undefined //=> false null == undefined //=> true
参考
2022/10/2(日) 現場Rails Chapter1_1 オブジェクトを理解してみよう
クラスとは
全てのオブジェクトが持つそのオブジェクトの原型のことです。
"おはよう".class => String
- 「おはよう」という文字列のクラスを調べると、Stringクラスということが分かります。
- classというメソッドを使って、どのクラスに属するのかということを調べます。
オブジェクトに対して、どんな機能を持たせるかは、そのオブジェクトがどのクラスに属するかによって決まります。クラスによって予め持っているメソッドなどが異なるためです。
"おはよう".length => 4 4.length (irb):4:in `<main>': undefined method `length' for 4:Integer (NoMethodError)
- 「おはよう」という文字列オブジェクトは、Stringクラスに属するため文字数を調べることができるlengthメソッドを使用することができます。
- 一方で、integerクラスに属する数値の4は、lengthメソッドを使用することができません。これは、integerクラスがlengthメソッドを持っていないからです。
今まで出てきたStringクラスやIntegerクラスのようにRubyが標準的に用意しているクラスの他にも、自分のやりたいことに合わせてクラスを作成することもできます。
クラスとインスタンス
クラスは、オブジェクトの原型であると説明しました。代わって、インスタンスは、クラスを元にして作られたオブジェクトのことです。 1クラスからは、インスタンスを生み出すことができ、作成されたインスタンスは、クラスが持つ属性やメソッドを持っています。
例)
分かりやすく例えると、クラスはたい焼きの金型であると言えます。金型を元にして、実際に作られるたい焼きがインスタンスになります。
クラスであるたい焼きの金型があることで、小倉あんのたい焼きや抹茶味のたい焼き、クリーム味のたい焼きなどの様々なたい焼きインスタンスを作り出すことができるのです。
変数
変数とは、コンピュータプログラムのソースコードなどで、データを一時的に記憶しておくための領域に固有の名前を付けたもの。
e-Words
label = "おはよう" => "おはよう" p label => "おはよう"
- あるオブジェクトを参照したい時にそのオブジェクトの参照口を指し示してアクセスできるように名前をつけたものです。
- labelという変数に文字列「おはよう」を代入すると、変数を使うだけで、文字列「おはよう」を参照できるようになります。
変数には、プログラム中の一定の処理の範囲で使われ、その範囲が終わったら捨てられるローカル変数とオブジェクトの内部に保持されてオブジェクトが存在する限り一緒に存在するインスタンス変数があります。
- ローカル変数とは、プログラム中のブロック(関数やメソッドなど)の内部で宣言され、その範囲内でのみ有効な変数のことです。
- インスタンス変数とは、個々のインスタンスごとの固有の変数です。インスタンスの情報が入った変数のことで、メンバ変数、フィールドとも言われます。
メソッド
オブジェクトの振る舞いをメソッドと言います。色々な処理をひとつにまとめてメソッドとして定義することで、何度も再利用(実行)ができるようになります。
インスタンスに対して、呼び出せるメソッドはインスタンスメソッドと呼ばれ、クラス内に定義します。インスタンスメソッドをクラス内に定義することで、別のインスタンスにも同じような振る舞いをさせることができるのです。
class Human # メソッドの定義 def walk puts "歩きます" end end # Humanクラスからインスタンスを生成 humanA = Human.new() # インスタンスに対して、インスタンスメソッドを呼び出す humanA.walk # => 歩きます
参考
2022/10/1(土) SPA, SSR, SSGの深堀り
MPAとは
ブラウザから「こういうページがほしい」とHTTPリクエストをもらうと、Webサーバーは内部でHTMLを組み上げ、JavaScriptやCSSと一緒にHTTPレスポンスを返します。重要なのは、この処理がページを遷移するごとに毎回行われるということです。
SPA, SSR, SSGって結局なんなんだっけ?
- SPAと対照的にMPAでは、 差分検出をすることなく、リクエストごとに毎回サーバー側でHTMLの作成が行われページ全体が更新されます。その結果、ページの更新に時間がかかるなどパフォーマンスが悪くなるデメリットがあります。
- MPAでのパフォーマンスの問題を解決するひとつとしてSPAがあります。
SPAとは
初回リクエスト時にページの基本となるHTMLやCSS、JavaScriptをサーバーから受け取り、その後は必要なデータだけをサーバーに要求し、その変更差分を表示する方式で、ページを更新します。
SPA, SSR, SSGの違いについて図解でまとめてみた
- ページ全体を読み込むのは、初回リクエスト時のみで、ページ更新時はサーバーからデータのみを受け取り変更差分を更新します。
- ページの更新時にサーバーから受け取るデータは、JSON形式(JavaScript)で非同期に受け取ります。(Ajax通信)
- ブラウザ更新時にJSON形式で受け取ったデータをもとにクライアント側(ブラウザ)で、HTMLを構築し、変更差分を反映させたページを作成します。
- SPAは、クライアント側でページを生成するため、CSR(client side rendering)とも呼ばれます。
- SPAは、更新時に非同期で必要なデータのみを受け取るため、更新時間を素早くできるなどのメリットがあります。
- 一方で、SPAは、SEOに弱いなどのデメリットもあります。
SPA(シングルページアプリケーション)サイトはGoogleに正しく認識されないため、SEO的には最悪だと言われてきました。そのため、SSR(サーバサイドレンダリング)をし、初回画面表示時は本来ReactやAngularが生成するhtmlを、サーバサイドでレンダリングしてクライアント側はそれを表示するだけ、という手法を用います。
SSR無しのReact・Angular製のSPAサイトはGooglebotにどれくらい認識されるのか?
SSRとは
ブラウザ上で初期データをレンダリングするのではなく、サーバー側でAPIを叩き、レンダリングまでを行ってからHTMLファイルを返却する。
SSRでは、初回リクエスト時にサーバーにリクエストが走ります。初回リクエストを受け取ったサーバーは、APIと連携をして初回データを反映したHTMLを構築し、そのHTMLをブラウザに返すレンダリングまでを行います。つまり、SSRでは、サーバー側でページを構築し、ページを返すレンダリングまでが行われるため、Server Side Renderingと呼ばれます。
2回目以降のリクエストは、直接ブラウザからAPIにアクセスしデータを取得しに行きブラウザ上でデータを反映したページを生成してレンダリングを行います。
SSRでは、SPAで挙げられる問題点を解決するためにSPAと組み合わせて使われるアーキテクチャです。 - 初回ページ表示までの時間が早い - SEOに強い
しかし、サーバー側の負担が大きくなるなどのデメリットもあります。
SSGとは
アプリケーションのビルド時(アプリケーションがサーバーにデプロイされるタイミング)にサーバーがAPIから必要な初期データ等を取得し、そのデータを反映したHTMLを作成します。
重要なのは、HTMLの作成が行われるタイミングがサーバーへのリクエスト時ではなく、ビルド時であることです。
よって、サーバーへリクエストがあると、ビルド時に作成済みのHTMLファイルを返却するだけなので、素早くページの更新ができるようになります。
SPA, SSR, SSGの違いについて図解でまとめてみた
- SSGでは、事前にビルドしたタイミングで、サーバーがデータ取得を行いレンダリングが行われるので、そのことをプリレンダリング(事前描画)と言います。
- レンダリングとは、表示用のデータをもとに、内容を整形して表示することです。
- SSGは、リクエスト時にかかるサーバーへの負荷がかからないのがメリットです。
- ただし、ページ量が多いWebサイトなどだと、ビルド時にかかるサーバーへの負担が大きくなります。
まとめ
- データを取得するには、APIへアクセスを行う
- SPAでは、ブラウザ側(クライアント)でページを生成するのに対し、SSR、SSGでは、サーバー側でページの生成を行う。
- SPAでは、ブラウザ側(クライアント)でページの生成が行われるため、CSRと呼ばれる
- SPA、SSR、SSGは、それぞれデータ取得のタイミングが異なるのと、ページ生成の場所が異なるだけです。
参考
SPAの基本と3大JavaScriptフレームワーク(Angular、React、Vue.js)の最新動向 ~2020年版~
Gitでブランチを切るのを忘れて間違えてmasterにコミットしてしまったときの対処法
誤ってmasterブランチでコミットしてしまっても、そのコミットの変更内容を別のブランチに移すことで対処できます
① どこのコミットまで変更内容を移したいのか確認する
# masterブランチのコミット履歴を1行で表示させます $ git log --oneline master 813c118 (HEAD -> master) Add: # 移動したい変更3 a7ba267 Add: 〇〇〇〇 # 移動したい変更2 3dfd56a Add: 〇〇〇〇 # 移動したい変更1 7dd0b34 (origin/master) Merge pull request 2a3701c (origin/development) ede71f6 Add:〇〇〇〇 8a05884 Add: 〇〇〇〇 a1f10f4 first-commit
上記より、7dd0b34
から上の3つのコミットが誤ってコミットしてしまった部分だと分かります。
② 変更の移動先となるブランチがまだ作成されていない場合は、移動先となるブランチを作成します
# ブランチ名を作成し移動 $ git checkout -b ブランチ名 # ブランチ一覧表示と今いるブランチの確認 $ git branch
③ git reset --hard
コマンドでmasterブランチから移動したい変更のコミットを削除します
今回は、3つのコミットを移動させたいので、masterブランチから最新の3つ分のコミット履歴を削除します。ただし、git reset --hard
を使う場合は、プッシュ前のコミットに対してのみ、使うようにしましょう。
HEAD
は、今自分がいるブランチの先頭のことで、自分が作業している場所を示すポインタです。
# masterブランチにいることを確認する $ git branch * master posts # masterブランチで、git reset --hardを使います。 # masterブランチの指定した時点まで「HEAD(今自分がいるブランチの先頭)・ステージングエリア・作業ディレクトリ」のすべてが巻き戻されます。(今回は3つ前まで) $ git reset --hard HEAD~3 HEAD is now at 7dd0b34 Merge pull request #1 from name/development # masterブランチのコミット履歴を1行で表示 $ git log --oneline master 7dd0b34 (HEAD -> master, origin/master) Merge pull request 2a3701c (origin/development) Add:〇〇〇〇 ede71f6 Add:〇〇〇〇 8a05884 Add: 〇〇〇〇 a1f10f4 first-commit
これで、今いるmasterブランチの先頭がリモートリポジトリの内容をプルした部分まで巻き戻りました。
④ 移動したいブランチで作業の続きを行います
masterブランチのコミットは削除しましたが、移動したブランチ内ではコミット履歴が残っていることが確認できました。
# masterからpostブランチへ移動 $ git checkout posts # ブランチ確認 $ git branch * master posts # postsブランチのコミット履歴を表示 $ git log --oneline posts 813c118 (HEAD -> posts) Add: 〇〇〇〇 # 移動した変更3 a7ba267 Add: 〇〇〇〇 # 移動した変更2 3dfd56a Add: 〇〇〇〇 # 移動した変更1 7dd0b34 (origin/master, master) Merge pull request 2a3701c (origin/development) Add:〇〇〇〇 ede71f6 Add:〇〇〇〇 8a05884 Add: 〇〇〇〇 a1f10f4 first-commit
参考
[Git]誤ったブランチで実施した変更を正しいブランチに移動する | DevelopersIO
第6話 git reset 3種類をどこよりもわかりやすい図解で解説!【連載】マンガでわかるGit ~コマンド編~ - itstaffing エンジニアスタイル
ローカルで新規プロジェクトを立ち上げてから、GitHubのリモートリポジトリに結びつけるまでの一連の手順
1. ローカルでプロジェクトを立ち上げたら、まずローカルで空のcommitを実行します。
Gitは、初回のコミット後に新しくブランチを作成できるようになります。そのためまずは、空のコミットを実行してGitの機能を使えるようにしていく必要があります。
① 空のコミットを実行
GitHubへアップロードしたいプロジェクトディレクトリへ移動してから、Gitの初期化を行います。git init後、作業ディレクトリでgitが使えるようになります。
# Gitの初期化 $ git init # 空のcommitを実行(--allow-emptyオプションを付けると空のコミットが実行できます。) $ git commit --allow-empty -m "ファーストコミット名"
⇒ 実行後、新しいブランチ(master)が作成されています。
② リモートリポジトリの登録
「git remote add origin リモートリポジトリの場所」で、現在のローカルリポジトリに指定したリモートリポジトリを登録します。
# 新しいリモートリポジトリをローカルと結びつける $ git remote add origin https://github.comユーザー名/リポジトリ名 #=> 現在のローカルリポジトリに、「origin」という名前で、https://github……という場所《URL》をリモートリポジトリとして追加する
2. ローカルで実行した空のコミットをローカルリポジトリにプッシュしていきます。
①ローカルのmasterブランチからリモートのmasterブランチへPush
$ git push -u origin master
⇒ 実行後、作業に入る前に新しくブランチを切ってから作業をしていきます。
3. gitを使用したブランチ作成からpushまでの簡単な流れ
ここからは、いつも通りの作業になります。作業ごとにブランチを分けてプッシュ、マージ後プルをしていきます。
① 作業を開始前に新しくブランチを切ります。
# 今いるブランチを確認(-aオプションをつけることでリモートブランチも見れます) $ git branch -a または $ git branch # 新しくブランチを作成 $ git branch ブランチ名 # 作成したブランチに移動 $ git checkout 作成したブランチ名 または、 # ブランチ作成と移動を同時に行う $ git checkout -b ブランチ名
② ファイルを編集後、コミットを実施する
# ファイルの編集状態確認(変更差分のあるファイルのみが出力されます) $ git status # 変更差分のあるファイル全てをコミット対象に追加(ファイルを個別に追加することもできます。) $ git add . # コミット対象に追加したファイルをコミットメッセージ付きでコミット $ git commit -m "コミットコメント" # コミット履歴を表示(--all --oneline --graphオプションを付けると見やすくなる) $ git log --all --oneline --graph
③ ローカルブランチでコミットしたファイルをリモートのブランチにプッシュする
-u
オプションは、ローカル側の現在いるブランチに紐づくリモート側のブランチを作成&登録します。–set-upstream
と同じ意味です。
オプションを付けて実行すると、今後リモートのブランチ名を指定せずにgit push
コマンドのみでgit push origin ブランチ名
と同じ実行ができるようになります。
# リモートリポジトリに指定した名前でブランチを作成し、そのブランチにプッシュを実施する $ git push -u origin ブランチ名 または、(HEADを付けるとブランチ名を書く必要がなくなります) $ git push origin HEAD
④ リモートリポジトリ側でpushしたコミットをmasterブランチにマージします。
特に問題がなければ、masterブランチにマージを行いましょう。 これでリモートのmasterブランチに最新のファイルが読み込まれました。
マージ後に、リモートの作業ブランチを削除するようにしましょう。
⑤ ローカル側のmasterブランチにリモート側のmasterブランチの内容をpullします。
# ローカルで作業ブランチからmasterブランチに移動 $ git checkout master # リモートのmasterブランチからマージしたファイルを取得する $ git pull origin master
参考
【GitHub】GitHubで新規リポジトリを作成後、最初のコミットは空で行いましょう。 - 文系Webエンジニアの技術メモとたまに旅のメモ
gitを使用したブランチ作成からpushまでの簡単な流れ - Qiita