Rails, заполнить базу данных связанными моделями в одной форме

#ruby-on-rails

#ruby-on-rails

Вопрос:

Я просмотрел различные ответы на похожие вопросы и не совсем понял это. wine Модель определяется с has_one :register, :dependent => :destroy помощью и правильно или неправильно я добавил accepts_nested_attributes_for :register . A register определяется с belongs_to :wine помощью .

Код внутри wines_controller.rb for create :

 def new
  @wine = Wine.new
  @register = Register.new

def create
  @wine = Wine.new(wine_params)
  @register = @wine.registers.build(register_params)
  respond_to do |format|
    if @wine.save 
    #success
    else
      format.json { render json: @wine.errors, status: :unprocessable_entity }
      format.json { render json: @register.errors, status: :unprocessable_entity }
    end
  end 
end
  

Моя форма для создания a new wine имеет следующий код:

 <%= simple_form_for @wine do |f| %>  
# various working elements
<div class="field">
  <% f.fields_for :register do |r| %>
    <%= r.label :short_name %>
    <%= r.text_field :short_name %>
    <%= r.label :barcode %>
    <%= r.text_field :barcode %>
  <% end %>
</div>
  

При вызове этой формы из команды не создаются поля f.fields_for , но этот блок выполняется, потому что я могу добавить в него тестовые кнопки, чтобы доказать, что к нему обращаются.

Если я пытаюсь создать wine, я получаю следующее сообщение об ошибке:

 undefined method `registers' for #<Wine:0x007f1204375330> Did you mean? register register= register_id
  

Я считаю, что использование .build предназначено для обеспечения целостности данных: я не хочу создавать a wine , у которого нет соответствующего register . Я пытался подумать об этом вложенные атрибуты, но, похоже, многие считают это плохим планом. Этот текущий подход кажется правильным, но я думаю, что мне, по крайней мере, не хватает понимания синтаксиса.

Позже необходимо будет связать другие модели register , которые не будут связаны с wines. Я рассматривал аналогичный подход, но я рад, что мне сказали переосмыслить!

Ответ №1:

Если я правильно вас понимаю, у вас есть 2 проблемы:

Во-первых, поля для register не отображаются — это отчасти потому, что @wine .регистр равен нулю.

Вы должны изменить свое новое действие на:

 def new
  @wine = Wine.new
  @wine.register = Register.new
  

Кроме того, поскольку вы используете simple_form_for , вам нужно будет использовать simple_fields_for вместо fields_for

Ваша вторая проблема, которая приводит к исключению, говорит вам все… вы пытаетесь получить доступ к @wine.регистры, а не @wine .Зарегистрироваться

Измените свой метод создания на:

   @register = @wine.register.build(register_params)
  

Это устранит эту проблему… однако … все, что вам действительно нужно сделать, это создать объект @wine из ваших параметров — ваши параметры должны быть настроены так, чтобы разрешать правильные вложенные атрибуты — если он настроен правильно, объект register также будет создан при создании объекта @wine .

Ваша модель уже настроена на accept_nested_attributes и, следовательно, также будет проверять и сохранять объект register при вызове @wine .сохранить — нет необходимости явно сохранять объект register.

У вас должно быть что-то вроде:

 def wine_params
    params.require(:wine).permit(
          :attribute1, :attribute2,
          register_attributes: [:id, :short_name, :barcode])
end
  

Комментарии:

1. Хорошо, спасибо за ввод. Первая предлагаемая вами корректировка приводит к появлению » ActiveModel:: MissingAttributeError в WinesController #new` и can't write unknown attribute 'wine_id'

2. Эта ошибка возникает, если я пытаюсь создать новую запись wine

3. не могли бы вы уточнить… мы говорим о @wine.register = Register.new строке? Если это так, проверьте свою таблицу регистров… у него должна быть ссылка на wine_id

4. В таблице wines действительно есть столбец wine_id , автоматически созданный Rails. Сообщение об ошибке указывает на строку @wine.register = Register.new

5. Я попытался добавить wine_id into the attributes of register`, но, к счастью, это не помогло. Я бы только смутился, если бы это было так!

Ответ №2:

Попробуйте это

Модели Wine и Register

 class Wine < ApplicationRecord
    has_one :register, inverse_of: :wine,  :dependent => :destroy
    accepts_nested_attributes_for :register
end

class Register < ApplicationRecord
    belongs_to :wine, inverse_of: :register
    validates_presence_of :wine
end
  

Контроллер Wines

 class WinesController < ApplicationController
  def new
    @wine = Wine.new
    @wine.build_register
  end


  def create
    @wine = Wine.new(wine_params)

    if @wine.save
        redirect_to @wine
    else
        render :new
    end
  end

  private
    def wine_params
        params.require(:wine).permit(:name, register_attributes: [:simple_name])
    end
end
  

Мои wine_params специфичны для

 rails g model wine name:string
rails g model register name:string wine_id:integer
  

Наконец, форма wine должна выглядеть следующим образом

 <%= form_for @wine do |f|%>
    <p>
        <%= f.label :name%>
        <%= f.text_field :name%>
    </p>

    <%= f.fields_for :register do |r|%>
    <p>
        <%= r.label :simple_name%>
        <%= r.text_field :simple_name%>
    </p>    
    <% end %>

    <%= f.submit %>
<% end %>
  

Таким образом, вы можете изменять wine_params и формировать partial для специфики вашего приложения

Комментарии:

1. регистрация будет сохранена автоматически при создании wine

2. Также вы можете использовать simple_form_for и simple_fields_for вместо form_for и fields_for