質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

ある既存モデルに関連付けられる新規モデルのレコードを一気に作成する方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 629

azuno_mix

score 71

 現在行っていること

現在、既存サービスにユーザーが任意に通知のあり・なしを管理できる機能を実装しています。
以下のようなモデルの作成を行いました。

 テーブル設計

create_table "user_notifies", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" do |t|
    t.integer  "user_id",                                  null: false
    t.boolean  "a_notify",                  default: true, null: false
    t.boolean  "b_notify",                  default: true, null: false
    t.boolean  "c_notify",                  default: true, null: false
    t.datetime "created_at",                               null: false
    t.datetime "updated_at",                               null: false
    t.index ["user_id"], name: "index_user_notifies_on_user_id", using: :btree
end

 モデル

class User < ApplicationRecord
    has_one :user_notify, dependent: :destroy, class_name: UserNotify
    after_create :create_user_notify, unless: proc { |user| user.user_notify }
end
class UserNotify < ApplicationRecord
    belongs_to :user, class_name: User
end

 問題となっていること

このような実装で、デプロイしてみたところ、設定画面で404が表示されるようになりました。

Processing by NotifySettingsController#edit as HTML
  User Load (6.7ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 5 ORDER BY `users`.`id` ASC LIMIT 1
  UserNotify Load (5.9ms)  SELECT  `user_notifies`.* FROM `user_notifies` WHERE `user_notifies`.`user_id` = 5 LIMIT 1

Completed 404 Not Found in 38ms (ActiveRecord: 12.6ms)
ActiveRecord::RecordNotFound (Couldn't find UserNotify)

原因としては、この実装以前に作成されたUserはUserNotifyのレコードを持っていないので、インスタンス取得が失敗し404となっているのかと思います。

def set_instance
  @user_notify = UserNotify.find_by!(user_id: current_user.id)
end

この問題自体は、以下のようにレコードがなければ作成するといったことで回避することはできると思います。

def set_instance
  @user_notify = UserNotify.find_or_create_by!(user_id: current_user.id)
end

実際、デバッグしてみたところ、うまくいきました。

Processing by NotifySettingsController#edit as HTML
  User Load (15.7ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 5 ORDER BY `users`.`id` ASC LIMIT 1
  UserNotify Load (2.2ms)  SELECT  `user_notifies`.* FROM `user_notifies` WHERE `user_notifies`.`user_id` = 5 LIMIT 1
   (2.8ms)  BEGIN
  User Load (1.3ms)  SELECT  `\users`.* FROM `users` WHERE `users`.`id` = 5 LIMIT 1
  SQL (14.6ms)  INSERT INTO `user_notifies` (`user_id`, `created_at`, `updated_at`) VALUES (5, '2017-05-25 05:06:53', '2017-05-25 05:06:53')
   (30.3ms)  COMMIT

...

Completed 200 OK in 1745ms (Views: 1393.2ms | ActiveRecord: 140.3ms)

 解決したいこと

一方で通知設定を用いてメールを送信するか判断しているMailer側の実装は以下のようになっています。

class NotificationMailer < ApplicationMailer
  def aaa_notify(user)
    users = User.joins(:user_notify).where(user_notifies: { aaa_notify: true })
    return if users.empty?
    mail bcc: users.pluck(:email)
  end
end

このメイラーの実装だと、UserNotifyモデルを含んだ実装後に新規登録したユーザであればレコード作成時(after_create)でUserNotifyにも通知設定レコードが作成されますが、既存ユーザの場合、この通知設定のページにアクセスしない限り、UserNotifyにレコードが存在しないため、無条件で通知されない(whereでの絞込に該当しない)問題があるかと思います。

こういった場合、「既存ユーザのUserNotifyを何らかのクエリを叩くことでレコードを一気に作成するか」「Mailerの実装を工夫する(UserNotifyで通知をfalseにしている人を通知対象から除外する方向での実装)」など、色々と考えていますが、知見を持っている方に一度意見を伺いたく質問させていただきました。

どうぞ、よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

私なら、
「既存ユーザのUserNotifyを何らかのクエリを叩くことでレコードを一気に作成する」
を選びますね。

今後、UserNotifyの存在可否を考慮しながら開発するのは面倒ですし、バグの温床にもなりますしね。。。

クエリ叩かなくても、コンソールから既存ユーザーに対して、create_user_notifyを実行(privateならuser.sendで)すればいいだけの話ですし。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/25 19:34

    h_daidoさん

    ご意見、ありがとうございます!
    > UserNotifyの存在可否を考慮しながら開発するのは面倒ですし、バグの温床にもなります
    確かにその通りだと感じます。今後の開発を考えていくと一発コンソールなどでUserNotifyを作成した方がコストもリスクも低いかもしれません!

    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る