Rails关联未插入嵌套属性

时间:2019-01-14 01:02:55

标签: ruby-on-rails associations

尽管在表单中指定了嵌套的body属性,但未插入该属性。尽管票证模型接受:message的嵌套属性,但我收到“ Unpermitted parameter::message”错误。票务控制器还允许message_attributes。

Processing by TicketsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"tTOKOw/q0H8gXyEnaEK8lmwZUAoMsI4QQAt4VTwG8QdtQNZtf8Yj7N6Z0VR6MZbvoUARzp7DG60dI5RuKtP8Fw==", "ticket"=>{"category"=>"Miscellaneous - Project", "deadline"=>"2019-01-13", "message"=>{"body"=>"sdf"}}, "button"=>""}
Unpermitted parameter: :message
(0.2ms)  BEGIN
↳ app/controllers/tickets_controller.rb:32
User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
↳ app/controllers/tickets_controller.rb:32
Ticket Create (0.8ms)  INSERT INTO `tickets` (`category`, `deadline`, `created_at`, `updated_at`) VALUES ('Miscellaneous - Project', '2019-01-13', '2019-01-14 00:45:52', '2019-01-14 00:45:52')
↳ app/controllers/tickets_controller.rb:32
Message Create (0.6ms)  INSERT INTO `messages` (`user_id`, `ticket_id`, `created_at`, `updated_at`) VALUES (1, 62, '2019-01-14 00:45:52', '2019-01-14 00:45:52')
↳ app/controllers/tickets_controller.rb:32
(116.8ms)  ROLLBACK
↳ app/controllers/tickets_controller.rb:32
Completed 500 Internal Server Error in 159ms (ActiveRecord: 120.3ms)



ActiveRecord::NotNullViolation (Mysql2::Error: Field 'body' doesn't have a default value: INSERT INTO `messages` (`user_id`, `ticket_id`, `created_at`, `updated_at`) VALUES (1, 62, '2019-01-14 00:45:52', '2019-01-14 00:45:52')):

app/controllers/tickets_controller.rb:32:in `block in create'
app/controllers/tickets_controller.rb:31:in `create'

new.html.erb

<%= form.fields_for @ticket.message do |message_form| %>
<div class="form-group row">
  <div class="col-sm-12">
    <%= message_form.label :body %>
    <%= message_form.text_area :body, class: "form-control" %>
  </div>
</div>
<% end %>

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable, :trackable, :rememberable, :validatable
  has_many :messages, inverse_of: :user
  has_many :tickets, through: :messages
end

ticket.rb

class Ticket < ApplicationRecord
  has_one :message, inverse_of: :ticket
  has_many :messages, inverse_of: :ticket
  has_many :users, through: :messages
  accepts_nested_attributes_for :message
end

message.rb

class Message < ApplicationRecord
  belongs_to :user, inverse_of: :messages
  belongs_to :ticket, inverse_of: :message
  belongs_to :ticket, inverse_of: :messages
  validates_presence_of :user, :ticket
end

tickets_controller.rb

def new
  @ticket = Ticket.new
  @ticket.build_message
end

def create
  @ticket = Ticket.new(ticket_params)
  @ticket.build_message(:user_id => current_user.id)

  respond_to do |format|
    if @ticket.save
      format.html { redirect_to @ticket, notice: 'Ticket was successfully created.' }
      format.json { render :show, status: :created, location: @ticket }
    else
      format.html { render :new }
      format.json { render json: @ticket.errors, status: :unprocessable_entity }
    end
  end
end

private
  def ticket_params
    params.require(:ticket).permit(:category, :deadline, :status, message_attributes: [:body])
  end

1 个答案:

答案 0 :(得分:1)

首先,您需要纠正关联。

同时拥有has_one :messagehas_many :messages只会使人感到困惑,并且很可能无法按照您的预期方式工作。

特别需要注意的是:

belongs_to :ticket, inverse_of: :message
belongs_to :ticket, inverse_of: :messages

您认为这段代码做什么?如果您猜测它会创建两个关联,那么您错了。如果您有两个同名的关联,则后面的关联会覆盖前一个。

我建议您砍掉has_one关联:

class Ticket < ApplicationRecord
  has_many :messages
  has_many :users, through: :messages
  accepts_nested_attributes_for :messages
end

class Message < ApplicationRecord
  belongs_to :user
  belongs_to :ticket
  validates_presence_of :user, :ticket
end

如果以后您想为最新消息(或第一条消息)添加单独的关联以进行急切加载,则需要将外键列放在票证表上并使用belongs_to

class Ticket < ApplicationRecord
  belongs_to :latest_message
  has_many :messages, after_add: :update_latest_message!
  has_many :users, through: :messages
  accepts_nested_attributes_for :messages

  private
  def update_latest_message!(msg)
    self.update!(latest_message: msg)
  end
end

然而,只有当您有大量邮件并且需要能够只加载最新消息时,这种优化才有意义。我将其留待以后使用。 过早的优化是万恶之源

关于代码当前失败的原因,您应该看一下参数:

{  "utf8"=>"✓", ... 
   "ticket"=>{ 
      "category"=>"Miscellaneous - Project", 
      "deadline"=>"2019-01-13", 
      "message"=>{"body"=>"sdf"}}, # should be messages_attributes
      "button"=>""
    }
}

您需要将表单设置为:

<%= form.fields_for :messages do |message_form| %>
  <div class="form-group row">
    <div class="col-sm-12">
      <%= message_form.label :body %>
      <%= message_form.text_area :body, class: "form-control" %>
    </div>
  </div>
<% end %>

并将其植入控制器:

def new
  @ticket = Ticket.new
  @ticket.messages.new # seed the form
end

即使用户一次只创建一条消息,您仍然希望使用消息关联。