ruby-jp slack で ActionText の質問に答えた
ruby-jp.slack.com | ruby-jp の #support チャンネルで ActionText に関する質問があったので調べて答えた。
ActionText はまだちゃんと触っていなかったので、調べるきっかけになってよかった。
質問内容
rails6の
ActionTextのrich_text_areaについて質問したいです!!!has_rich_textを使ったフォームでCreateするとエラーが出てしまいます(Updateでは正常に更新できます)。
エラー文
SQLite3::ConstraintException: NOT NULL constraint failed: articles.body
models/article.rb
class Article < ApplicationRecord has_rich_text :body validates :title, presence: true validates :body, presence: true end
そのほか views と controllers のコードも貼り付けてくれていた。
調べたこと考えたこと
ActionText か〜。まだ触ってないな〜。と思ってまずRailsガイドをみた。
読んでみると
リッチテキストコンテンツは独自のRichTextモデルに保存され、このモデルはアプリケーションの既存のあらゆるActive Recordモデルと関連付けられます。
と書いてあるから、今回のケースで言うと Article モデルの body フィールドの実体は RichText モデルに保存されるってことだな。
つまり articles テーブルには body カラムは必要ない、ということだ。
で、質問のエラー文をみてみると NOT NULL constraint failed: articles.body とある。
ん、これは articles モデルに body カラムがあるってことだなぁ。
てことは body カラムを作る migration をしたのだろう。
そして質問者さんは「この body カラムをリッチテキストにしたい!」と考えて has_rich_text :body をつけたのだろう、と想像した。
さらに質問を読み返してみると
フォームで
Createするとエラーが出てしまいます(Updateでは正常に更新できます)。
とある。
Create ができないのに Update ができる、とはどういうことか。
というか Create ができないのに、その Update の対象になっているレコードはどうやって作ったのか。
おそらくそのレコードは has_rich_text :body をつける前に作成したのだろうなと思った。
has_rich_text :body をつける前に作成したレコード( body カラムが not null なもの)であれば、has_rich_text :body をつけた後に Update しても not null 制約に掛からないのでエラーにならない。
回答
以上を踏まえて次の3点を回答した。
- 「もともと Article モデルにあった body カラムを rich_text にしようとしているのかな?」と推測したよ
- body カラムの実体は RichText モデルに保存されるから articles テーブルには body カラムは不要だよ
- articles テーブルの body カラムを消す migration をすれば
Createのときもエラーじゃなくなると思うよ
- articles テーブルの body カラムを消す migration をすれば
Updateがうまくいくのはhas_rich_text :bodyをつける前に作ったレコードだからだと思うよ
質問者さんの反応は
推測通りです!笑 DBをRollbackし、Articleモデルからbodyを削除してMIgrationしたらエラーが消えました!
あってた。わーい。
めでたし。
ではない。
重ねて質問させていただきたいのですが、 bodyをnull: falseにしたい場合は、どうすればよろしいのでしょうか?
ですよね。
has_rich_textに指定したフィールドのnullを禁止するには
ActionText::Attribute をみると has_rich_text の引数は name だけでオプションは指定できない。
null を禁止したければカスタムバリデーションを書くかなぁ、と思った。
質問者さんの Article モデルを書き換える形で書くと、こんな感じかな。
class Article < ApplicationRecord has_rich_text :body validates :title, presence: true validate :body_required private def body_required errors.add(:body, "is required") unless body.body.present? end end
body.body.present? となるのがなんとなく見栄えが悪いけど仕方ない。
なぜなら Ariticle モデルのフィールドとしての body は RichText モデルへの relation であり、 RichText モデルは body フィールドを持つので body.body になってしまう。
Article モデルのフィールド名を変えて body 以外(例えば content など)にするとこの見栄えの悪さを解消できる。
class Article < ApplicationRecord has_rich_text :content validates :title, presence: true validate :content_required private def content_required errors.add(:content, "is required") unless content.body.present? end end
Action Text の概要 - Railsガイド の例でも content というフィールド名を使っているのでなんとなく良さそう。
思ったこと
ruby-jp 基本的には眺めているだけだったけど質問したり答えたりできる場があるのは便利だなぁと思った。
あと ActionText について調べるきっかけになってよかった。
おしまい。
更新情報
2021-02-19
最後のコード例でのtypoを修正しました
class Article < ApplicationRecord - has_rich_text :body + has_rich_text :content validates :title, presence: true