00:00
yayamatoさんです。
今回のトピックは、 Rails のモデルのバリデーション、
association 先が invalid だと自身も invalid になる、ということに違和感がある、です。
今回は非常に長いタイトルなんですけど、 Rails の Active Record のモデルは、
だいたいバリデーションというのを設定しますよねと、
文字数 例えば その そうですね ブックっていうモデルがあって タイトルっていう属性があって
で タイトルの長さは何文字いないとか そういうのが だいたいバリデーションになりますよね
で 今回のその アソシエーション先がインバリットだと 自身もインバリットになるって どういうことかっていうと
わかりやすい例で言うと EC サイトでいきますかね EC サイトには商品っていうモデルがあります
で その商品っていうのは出品者によって市場に出回って購入できる状態になりますと
で この出品者っていうモデルなんですけど あの 何らかの理由で利用停止になることがあります
で ビジネスロジックとしては 利用停止になった出品者は 新しい商品を出品できないし 既存の商品を編集することもできないというのがありますと
で こういった時に だいたいそのバリデーションっていうのが使われるんですけど
商品自体には 例えば商品の名前だとか 商品の説明だとか そういったものがあるんですけど
今回は商品と関連のある出品者の状態によって バリデーションをセットしないといけないんですよね
なのでカスタムバリデーションみたいな そういうのが必要になると だからそのバリデーツじゃなくてバリデートですね
で バリデーションを定義してあげると バリデートとかで定義するときに バリデートに渡すメソッドの中で
出品者の状態を確認して その時に出品者が利用停止だったら 商品がインバリットだと説得するというロジックをかけますと
で これ よくあるような気がしているんですけど なんか
よくよく考えてみると結構違和感があるんですよね もちろんこれによって 商品と関連のある出品者が利用停止になった時には
その商品のクリエイトとかアップデートをストップすることができるので ビジネスロジックの要求は満たすんですけど
ビジネスロジックを知らない状態でこのコードだけ読むと 出品者が利用停止の場合 商品はインバリットになるって書いてあるということなんですよね
で これなんか違和感があるような気がしていて 違和感がありませんかと まあない人もいるかもしれないですけど 私は違和感を感じるんですよね
03:05
で 何で違和感を感じるかというと 例えば商品をfindっていうクラスメソッドですね で デビから取得して その直後にvalid?っていうそのバリデーションのチェックをする
メソッドを実行するとフォルスが返ってくるんですよ 何でかっていうと 関連先の出品者が利用停止になっているから
でもよくよく考えてみれば 商品自体は多分何の問題もないはずなんですよね 商品の名前とか 商品の説明とか
その商品自体に属するメタデータっていうものがいくつかあると思うんですけど そういったものが特に何の問題もない そういったデータは正しいにも関わらず
その関連先の出品者が利用停止になっていることによって 商品がインバリットになると これについて 今の職場の同僚に聞いてみたところ
RailsにはvalidatesAssociatedっていうメソッドがもともとあって これは別のアソシエーションの状態によってvalid?invalidが変わるっていうものらしくて
だから何でしょうね Rails これがRailsっぽいかRailsっぽくないかで言ったら そういうメソッドもあるぐらいだから
Railsっぽいんじゃないのって言われたんですよね 自然なことなのではと これを聞いてもなお まあやっぱ違和感があるんですよね
もはやこれはRails云々というよりはロジックの組み方のような気もするんですけど とりあえず一回Railsに軸足をいって
考えてみたいなと その違和感 言語化がうまくできないんで RailsGuidesっていうのを読んでみました
RailsGuidesのvalidationのところですね Active Record Validationsっていうページがあって そこのWhy Use Validationっていうセクションがあると
ここにvalidations are used to ensure that only valid data is saved into your databaseって書いてあって
何言ってるかっていうと 正しいデータのみがDBにセーブされることを保証するためにvalidationが使われるよって言っていますと
つまりデータが正しい時だけセーブする データが正しい時だけクリエイトしたり データが正しい時だけアップデートするって言ってるんですよね
validates associatedっていうところに例があって まずライブラリーっていうモデルがあって そのモデル
ライブラリーはas many booksっていう風になってるんですよね つまりライブラリーが複数のブックの関連を持っている状態
さらにas many booksと一緒にvalidates associated booksって これがまさにvalidates associatedの使い方の例なんですよね
06:06
これが例として書かれていて この状態だとライブラリーっていうモデルがあって それのas manyのたくさんのブックがあるんですけど
そのブックの一冊でもインバリットになると ライブラリー自体がインバリットになっちゃうっていうものなんですよね
使い方の例としては完璧なんですけど なんかこのロジックって自体がそもそも違和感ないですかねと
ライブラリーの固有の情報ですよね さっきも例えば商品で言いましたけど ライブラリーの固有の情報で言うと例えば住所とかですかね
図書館なんで住所かな 図書館の場所を移動しますと だから住所書き換えますといった時に
そのライブラリーが抱えている本の一冊でもインバリットなものがあると 住所書き換えられないんですよね
これちょっと違和感ないですかってやっぱり思っちゃうわけですよね もうやっぱりアプリケーションによるっていうような感じの
もはやフレームワークとか関係なく もう本当ビジネスロジック自体の欠陥なんじゃないかっていう気もするんですけど
そうですねバリデーツアソシエティットで使われていたライブラリー これのハイカーのブックスのうちの一つでもダメになるとセーブできない
なんか違和感ですよね ライブラリー自体が正しかったら別にセーブできてもいいんじゃないのって思うんですよね
そこでバリデーツアソシエティットなんか使うのやめればっていう話にはなると思うんですけど
さっきのECサイトで言うと商品と出品者がいて ビジネスロジックとしては
利用停止の出品者は商品を出したり編集したりできないと これってバリデーションでやるものではもはやないんじゃないかっていう気もしますよね
例えば またライブラリーとブックのさっきのバリデーツアソシエティットの例のところで
これを想像でちょっとしゃべりますけど ライブラリーハイカーのブックスってどれか一つでもインバリットだったらライブラリーをインバリットにするっていうのがあるんですけど
確かにインバリットな本 インバリットな本って何でしょうね 例えば昔で言うと検閲に引っかかった本とかですかね
それが一冊でもある図書館っていうのは閉鎖しなければいけないみたいな そういうロジックがもしかしたらあるのかもしれないですね
なんですけどでもやっぱりライブラリーのメタデータ 住所だとか あといろいろありますよね
移設したら多分ライブラリーの床面積とかも更新されるから それもちゃんと更新したりしないといけないかもしれないですよね もし属性として持ってるんだったら
そういったものって保存されていい気がするんですよね 更新されていい気がすると
さっきRailsGuysのバリデーションのページのところを読みましたけど もう一回改めて言うと正しいデータのみがDBにセーブされることを保証するためにバリデーションが使われると言ってるんですよね
09:04
正しいデータなんですよ ライブラリーって 関連しているブックの状態によってライブラリーって正しくなくなっちゃうっていう そのビジネスロジック自体なんか
なんかおかしいんじゃないのっていう気がしますよね 私の意見としては別モデルの状態に依存するバリデーションって何か慎重になった方がいいんじゃないかなっていう気がするんですよね
これ 実は私の現場でも似たような問題が出て そのバッジ処理とかで定期的に実行するスクリプトっていうのがあるんですけど それが失敗しちゃったりするんですよね
さっきのECサイトもそうかもしれないですけど 例えばそのECサイトでのがあるんだけど 毎日何かしらのその商品の何かしらのデータを更新するスクリプトがあって
そのスクリプトはもう単に商品の状態を更新するだけなんだけど そのスクリプトが走っているのに気づかずにさっきのビジネスロジック
利用停止した出品者は商品の作成更新ができないっていう風にしちゃうと 途端にそのスクリプトがエラーになる時があるんですよね
そのスクリプトは多分その商品だけで find each みたいなのをしてアップデートとかするんですけど
実はその商品に関連がある出品者情報が利用停止になっていると そのスクリプトがエラーになって
なんだこれはって言ってみんなで大騒ぎかどうかわかんないですけど 問題が出ますよねというところで
なんか結構そのドメイン境界をクロスしちゃう クロスしやすいですよねっていうのがあって
なんか慎重になりたいなっていう気がしますよね まあ本当にバリデーションというのはビジネスロジック記述するのに最もお手軽で最も効果的な方法ではあると思うんですけど
なんか注意した方がいいんじゃないかなっていう気がするんですよね じゃあこれどうやったら正解なのっていうと
まあわかんないですけどコンテキストによってモデルを分けてもいい気がするんですよね まあドメインによってって言い方でもいいかもしれない例えば
出品者が商品を作成更新するっていうドメインがきっとあるはずなんですよね その出品者向けの画面みたいなのがあってみたいな
でそこのドメインではおそらくそのビジネスロジックが適用されるべきなんですよ
でそしたら多分 そのドメインの商品というのがあってしかるべきだからまあ
何でしょうねネームスペース切ってそのネームスペース配下に商品ってやってみたいな感じですかね 一方でさっきのその
クーロンバッジスクリプトのところはまた別のドメインな気がするんですよね そこでは多分さっきの利用停止出品者が商品を更新作成できないっていう
12:01
ビジネスロジックが適用する必要がないから まあそのバリデーションのない別ドメインの商品というモデルがあってもいいんじゃないかな
という気がします Rails ってテーブルクラスっていう
えっと何でしたっけ あとアクセサーがあるはずなのでモデルの名前を適当なものにしてもテーブルクラスで指定すれば
そのテーブルの情報を引っこ抜けてつまり2つの複数のモデルが同一のテーブルを参照してやるっていうことができるんですよね
わかりづらいかもしれないんですけどただドメイン境界を分けるっていう観点では結構効果的な やり方なんじゃないかなっていう気がしますと
こういうアイディアを同僚とちょっとしゃべろうかなって思って まあなんかまずその
まず最初の冒頭の アソシエーション先がインバリットだと自身もインバリットになるっていうことに対する違和感を伝えて
なんか ここに興味を持ってくれた人がいたのでワークショップを行ったんですよね
ワークショップって言ってもまあアイディアだしみたいなこれどうですかみたいな やったんですけどちょっと私の走り力がいまいちで
アイディアが一つしか出なくてちょっと申し訳なかったなと 1時間ぐらい時間取ったんですけど申し訳なかったと
本当に走り力の未熟さを思い知らされました 私の中でそのさ今言ったアイディアを
言わないように頑張ったんですよね それはファシリテーターに徹するということができたので良かったのかなとは思うんですけど一方で
ファシリテーターの帽子被りつつ参加者の帽子も被って参加者としての意見を出すっていうことも やれるともうちょっとそのワークショップが
ワイワイして良かったんだろうなっていう気がして ファシリテーションって難しくもうちょっとうまくなりたいなという余談ですね
あとこの話を出した時にあるメンバーがですね そもそもお前は何で具体的に悩んでるんだみたいなこと言ってて
まあ私が確かにその抽象化した 抽象化した言い方をするとあるモデルがあってそのモデルのアソシエーション先が
インバリットだとそのモデルもインバリットになるという状態これに対する違和感みたいな感じで めちゃくちゃその抽象化した状態で言ったんですけど具体的な事例を知りたい
みたいな感じで言ったんですよね それはそれでいいんですけどそこからその具体的な事例を解決されるための
なんかこうソリューションとかを出してくれたんですけど 私としてはそこのソリューション正直もう興味ないというか
それはもう何かいくらでもやり方はあるんですよ バリデーションのコンテキストを使うとか操作者っていうオブジェクトをメソッドに渡して
そのメソッドの状態を見てバリデーションを適用する適用しないみたいなことはあると思うんですけど
そういったそのいわばバータリ的なそのやり方じゃなくてそもそも理想的な話をしてるんですよね
15:01
なので 具体の話をこう
何言って聞かれてすげー鬱となったっていうの経験がありました これあのなんかコミュニケーションとしてちょっと興味深いなって思うんですけど
私もそういうことが多分あるんだろうなって思うので なんかもうある程度抽象化した話を出してきた人はもう具体について興味ないというか
具体についての解決はすでにしている上でさらに理想を追い求めているかもしれないので
あんまりその具体は何なのって踏み込みすぎるのも鬱となって良くないんだろうなということで
これは自分自身のコミュニケーション方法としても反省したいなと思っております
ということで今回はレールズのモデルのバリデーション
アソシエーション先がインバリットだと自身もインバリットになるということに違和感があるでした
それではまた