Rails ассоциации не вставляют вложенный атрибут - PullRequest
0 голосов
/ 14 января 2019

Вложенный атрибут body не вставляется, хотя он указан в форме. Я получаю сообщение об ошибке «Unpermitted параметр:: сообщение», хотя модель заявок принимает вложенные атрибуты для: сообщения. Контроллер билетов также разрешает 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 голосов
/ 14 января 2019

Сначала вам нужно исправить свои ассоциации.

Наличие has_one :message и has_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

Даже если пользователь создает только одно сообщение за раз, вы все равно хотите использовать ассоциацию сообщений.

...