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

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

ただいまの
回答率

89.12%

assign_attributesメソッドでDBに保存しようとしてしまう

解決済

回答 1

投稿 編集

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

tenten11055

score 56

解決したいこと

編集画面でボタン押下時にバリデーションを表示させたいのですが、
アクション内のassign_attributesでDBに保存しようとしてしまいます。
(編集確認画面遷移前のバリデーションです)

ソース

module Api
  module V1
    class SuAccountsController < ApiController
      before_action :set_su_account, only: [:show, :edit, :update_confirm]

      def show
      end

      def edit
      end

      def update_confirm
        # debugger
        @su_account.assign_attributes(update_su_account_params)
        if @su_account.validate
          render json: @su_account, status: :ok
        else
          render json: { errors: @su_account.errors.keys.map {
            |key| [ key, @su_account.errors.full_messages_for(key) ]
          }.to_h, render: 'show.json.jbuilder' }, status: :unprocessable_entity
        end
      end

      def update
      end

      private

      def set_su_account
        @su_account = params[:su_account].present? ?
          SuAccount.find(params[:su_account][:id]) : SuAccount.find(params[:id])
      end

      def update_su_account_params
        params.require(:su_account).permit(
          :id,
          :group_name,
          :image_url,
          su_company_attributes: [
            :su_account_id,
            :company_name,
            :company_hp,
            :establishment_on,
            :establishment_year,
            :establishment_month,
            :establishment_date,
            :representative,
            :business_description,
            :capital,
            :capital_unit,
            :address
          ],
          user_attributes: [
            :su_account_id,
            :email,
            :last_name,
            :first_name,
            :tel
          ]
        ).tap do |v|
          year = params[:su_account][:su_company_attributes][:establishment_year]
          month = params[:su_account][:su_company_attributes][:establishment_month]
          date = params[:su_account][:su_company_attributes][:establishment_date]
          v[:su_company_attributes][:establishment_on] = "#{year}/#{month}/#{date}"
        end
      end
    end
  end
end


APIで「update_confirm」アクションにパラメータをPOSTしています。

app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'
Started POST "/api/v1/su_accounts/update_confirm" for 127.0.0.1 at 2019-12-05 12:36:02 +0000
Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by Api::V1::SuAccountsController#update_confirm as JSON
  Parameters: {"su_account"=>{"id"=>1, "group_name"=>"良い部署", "image_url"=>"", "su_company_attributes"=>{"su_account_id"=>1, "company_name"=>"", "company_hp"=>"https://hoge.com", "establishment_year"=>2019, "establishment_month"=>11, "establishment_date"=>1, "representative"=>"ほげ太郎", "business_description"=>"ほげほげの開発", "capital"=>2000000, "capital_unit"=>"us_dollar", "address"=>"ほげ県ほげ市"}, "user_attributes"=>{"email"=>"hogehugaa@example.com", "last_name"=>" ほげ太郎", "first_name"=>"ほげまつ", "tel"=>nil}}}


そしてアクション内の@su_account.assign_attributes(update_su_account_params)

  SuAccount Load (1.1ms)  SELECT `su_accounts`.* FROM `su_accounts` WHERE `su_accounts`.`deleted_at` IS NULL AND `su_accounts`.`id` = 1 LIMIT 1
  ↳ app/controllers/api/v1/su_accounts_controller.rb:31:in `set_su_account'
  SuCompanyInfo Load (0.6ms)  SELECT `su_company_infos`.* FROM `su_company_infos` WHERE `su_company_infos`.`deleted_at` IS NULL AND `su_company_infos`.`su_account_id` = 1 LIMIT 1
  ↳ app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'
   (0.4ms)  BEGIN
  ↳ app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'
  SuCompanyInfo Update (1.6ms)  UPDATE `su_company_infos` SET `su_company_infos`.`su_account_id` = NULL, `su_company_infos`.`updated_at` = '2019-12-05 12:36:02.686532' WHERE `su_company_infos`.`id` = 1
  ↳ app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'
   (0.6ms)  ROLLBACK
  ↳ app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'
Completed 500 Internal Server Error in 112ms (ActiveRecord: 12.3ms | Allocations: 26144)



ActiveRecord::NotNullViolation (Mysql2::Error: Column 'su_account_id' cannot be null):

app/controllers/api/v1/su_accounts_controller.rb:14:in `update_confirm'


しっかり保存しようとしています。
nullだから保存できないカラムは更新したくないカラムだし、
そもそもassign_attributesって保存はしないんじゃないの・・・と思っています。

@su_accountにパラメータを渡し、保存はせずにバリデーションをかけ、
エラー内容をjsonで受け取りたいのです。

なぜ保存されてしまうのか、教えていただけると助かります。
宜しくお願い致します。

※ストロングパラメーターのsu_account_idはあってもなくても同じ結果でした。

モデルを追記しました。
リレーションが怪しい気はしています。。

class SuAccount < ApplicationRecord
  # 論理削除使用
  acts_as_paranoid

  ##
  # relations
  ##

  has_one :su_company, class_name: 'SuCompanyInfo'
  has_one :user, class_name: 'User'

  accepts_nested_attributes_for :su_company, allow_destroy: true
  accepts_nested_attributes_for :user, allow_destroy: true

  ##
  # validates
  ##

  # 部署名
  validates :group_name,
    presence: true,
    length: { maximum: 100 }

  # URL形式チェック
  if Rails.env == 'production'
    validates :image_url, format: /\A#{URI::regexp(%w(http https))}\z/
  end
end
class SuCompanyInfo < ApplicationRecord
  # 論理削除用
  acts_as_paranoid

  ##
  # relations
  ##

  belongs_to :su_account, optional: true

  ##
  # validates
  ##

  # 企業名
  validates :company_name,
    presence: true,
    length: { maximum: 100 }

  # 企業HP
  validates :company_hp,
    length: { maximum: 2048 },
    format: /\A#{URI::regexp(%w(http https))}\z/

  # 設立年月日
  validates :establishment_on,
    date: true,
    reduce: true

  validates :establishment_year,
    presence: true, if: -> { establishment_month.presence || establishment_date.presence }

  validates :establishment_month,
    presence: true, if: -> { establishment_year.presence }

  validates :establishment_date,
    presence: true, if: -> { establishment_year.presence }

  # 代表者
  validates :representative,
    length: { maximum: 50 }

  # 事業内容
  validates :business_description,
    length: { maximum: 500 }

  # 資本金
  validates :capital,
    length: { maximum: 20 },
    numericality: {
      only_integer: true,
      allow_blank: true
    }

  # 通貨単位
  validates :capital_unit,
    presence: true, if: -> { capital.presence }

  # 所在地
  validates :address, length: { maximum: 200 }



  ##
  # enums
  ##

  enum capital_unit: {
    us_dollar: 1,
    yen: 2,
    yuan: 3
  }


  attribute :establishment_year
  attribute :establishment_month
  attribute :establishment_date

end
class User < ApplicationRecord
  # 論理削除使用
  acts_as_paranoid

  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :lockable, :password_expirable, :secure_validatable, :password_archivable, :expirable
  include DeviseTokenAuth::Concerns::User

  ##
  # relations
  ##

  belongs_to :su_account, optional: true

  ##
  # enums
  ##

  enum account_type: { DTVS: "01", DTVSCM: "02", Company: "11", CompanyPIC: "12", SUUser: "21" }, _prefix: true

  ##
  # validates
  ##

  validates :account_type,
    presence: true

  validates :password,
    password_complexity: true,
    if: :password_required?

  # 姓
  validates :last_name,
    presence: true,
    length: { maximum: 20 }

  # 名
  validates :first_name,
    presence: true,
    length: { maximum: 20 }

  # メールアドレス
  validates :email,
    presence: true,
    length: { maximum: 256 },
    email: true,
    uniqueness: true, on: :create


  # 電話番号
  validates :tel,
    allow_blank: true,
    length: { maximum: 25 },
    format: { with: /(\d|-|\+).*/ }

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

check解決した方法

0

モデルのリレーションで

  accepts_nested_attributes_for :su_company, allow_destroy: true, update_only: true
  accepts_nested_attributes_for :user, allow_destroy: true, update_only: true


のようにupdate_only: trueを付与したところ解決しました。

参考にした記事
has_oneな関連をaccepts_nested_attributes_forしている場合にattributesで代入した場合idが無いとdestroyされる

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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