Мой метод #build не сохраняется в базе данных - PullRequest
0 голосов
/ 02 августа 2020

Надеюсь, я хорошо это объясню. Я пытаюсь создать форму в своем приложении rails для создания полета и назначить для него пилота. Я все время получаю сообщение "undefined method` build 'for nil: NilClass ".

Вот моя летная модель

class Flight < ApplicationRecord
    belongs_to :pilot
    belongs_to :passenger
    accepts_nested_attributes_for :pilot

end

, а вот моя пилотная модель

class Pilot < ApplicationRecord
    has_many :flights
    has_many :pilots, through: :flights
    
    
end

Я подозреваю, кстати, что это может иметь какое-то отношение к тому, как настроены мои отношения, но мне трудно это доказать.

Это то, что у меня есть для моего метода #new в моем FlightController, когда я впервые начал его создавать

def new
        @flight = Flight.new
        @flight.pilot.build
end

Но сначала я переместил строку «@ flight.pilot.build» в свой метод #create, так как именно там он сохраняет ее в базе данных, но это было по-прежнему дает мне тот же «неопределенный метод build» для nil: NilClass »

def create
        @flight = Flight.create(flight_params)
        @flight.pilot.build
        if @flight.save
            redirect_to flight_path(@flight)
        else
            render 'new'
        end
    end


private

    def flight_params
        params.require(:flight).permit(:flight_number, :destination, pilot_attributes: [:name, :rank])
    end

Вот вид, на котором я пытаюсь выполнить задачу создания полета.

<h1>Create New Flight</h1>

<%= form_for(@flight) do |f|%>
<div>
<%= f.label :flight_number %>
<%= f.text_field :flight_number %>
</div><br>

<div>
<%= f.label :destination %>
<%= f.text_field :destination %>
</div><br>

<div>
<label for="flight_pilot_id">Assign a pilot:</label>
<%= f.collection_select :pilot_id, Pilot.all, :id, :name, {:prompt => "Select a Pilot"} %>
</div><br>


    <h3>Or Create new Pilot:</h3>
    <%= f.fields_for :pilot, Pilot.new do |pilot_attributes|%>
        <%= pilot_attributes.label :name, "Pilot name:" %>
        <%= pilot_attributes.text_field :name %>
        <br>
        <%= pilot_attributes.label :rank, "Rank:" %>
        <%= pilot_attributes.text_field :rank %>
        <br>
    <% end %>



<%= f.submit %>

<% end %>

Что я делаю не так?

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

В отличие от ассоциаций has_many, вы не можете создавать новые экземпляры из ассоциации has_one/belongs_to, поскольку она равна нулю, если вы не назначили ассоциацию.

Вместо этого has_one/belongs_to методы класса сгенерируйте следующие методы:

                                  |            |  belongs_to  |
generated methods                 | belongs_to | :polymorphic | has_one
----------------------------------+------------+--------------+---------
other                             |     X      |      X       |    X
other=(other)                     |     X      |      X       |    X
build_other(attributes={})        |     X      |              |    X
create_other(attributes={})       |     X      |              |    X
create_other!(attributes={})      |     X      |              |    X
reload_other                      |     X      |      X       |    X

Ваш контроллер должен на самом деле читать:

def new
  @flight = Flight.new(flight_params)
  # this just seeds the inputs
  @flight.build_pilot 
end

def create
  @flight = Flight.new(flight_params)
  if @flight.save
    redirect_to @flight
  else
    # this just seeds the inputs
    @flight.build_pilot unless @flight.pilot 
    render :new
  end
end

Как вы можете видеть здесь, построение действительно имеет мало общего с вставкой в ​​базу данных. Вместо этого он просто заполняет поля в форме, которые не будут присутствовать, если связь равна нулю. Вложенные атрибуты заботятся о фактическом создании записи полета. Не указывайте второй параметр для fields_for, поскольку он не обеспечивает значение по умолчанию - он перезапишет любую существующую запись, которая была там.

<%= f.fields_for :pilot do |pilot_attributes| %>
  <%= pilot_attributes.label :name, "Pilot name:" %>
  <%= pilot_attributes.text_field :name %>
  <br>
  <%= pilot_attributes.label :rank, "Rank:" %>
  <%= pilot_attributes.text_field :rank %>
  <br>
<% end %>

Если вы хотите назначить существующие записи, вам просто нужно передать и внесите в белый список параметр pilot_id:

<%= form_with(model: @flight) do |f| %>
  <%= f.collection_select(:pilot_id, Pilot.all, :name, :id) %>
<% end %>
def flight_params
  # be nice to maintainers and don't write your strong parameters 
  # in one mega-line
  params.require(:flight)
        .permit(
          :flight_number, :destination, :pilot_id,
          pilot_attributes: [:name, :rank]
        )
end

Однако это может вызвать проблемы, если пользователь передает и pilot_id, и вложенные атрибуты, поэтому вам придется иметь дело с этой возможностью.

0 голосов
/ 02 августа 2020

belongs_to ассоциация возвращает связанный объект или nil. В вашем случае вы используете вложенные атрибуты для pilot. Вам нужно удалить @flight.pilot.build, тогда пилот будет создан из flight_params при вызове Flight.create(flight_params). Это может зависеть от вашей версии Rails, вам нужно проверить свою модель Flight, чтобы иметь accepts_nested_attributes_for :pilot

Кроме того, я считаю, что вам нужно иметь pilot_id в flight_params чтобы иметь возможность назначить существующего пилота.

params.require(:flight).permit(:flight_number, :destination, :pilot_id, pilot_attributes: [:name, :rank])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...