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