Использование fields_for в Rails 4 не сохраняет новые поля

Я пытаюсь использовать вспомогательный метод fields_for в проекте, над которым работаю. Оригинальная форма работает и сохраняет просто отлично. Новые атрибуты не сохраняются, и я получаю NoMethodError и неопределенный метод. Что мне не хватает?!

Вот моя модель листинга:

class Listing < ActiveRecord::Base
has_one :listing_commerical_attribute
accepts_nested_attributes_for :listing_commerical_attribute, :allow_destroy => true

Вот моя модель listing_commercial_attribute:

class ListingCommercialAttribute < ActiveRecord::Base
  belongs_to :listing
  accepts_nested_attributes_for :listing
end

Вот мой контроллер:

def new
  @listing.build_listing_commercial_attribute

  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @listing }
  end
end

private

def commercial_params
  params.require(:commerical_listing_attribute)
      .permit(:gas_pipe_size,
              :amperage,
              :basement_ceiling_height,
              :ceiling_height,
              :door_size,
              :zoning,
              :previous_use,
              :community_board,
              :delivery_date,
              :key_money,
              :security_deposit,
              :price_per_sq_ft,
              :did_size)
end

Вот мой _form.html.erb:

<h2 class="text-center">Commercial</h2>
 <%= f.fields_for :listing_commerical_attributes do |ff| %>
<div class="field">
  <%= ff.label :gas_pipe_size, "Gas Pipe Size", class: "general-text-label" %>
  <%= ff.number_field :gas_pipe_size, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :amperage, "Amperage", class: "general-text-label" %>
  <%= ff.number_field :amperage, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :ceiling_height, "Ceiling Height", class: "general-text-label" %>
  <%= ff.number_field :ceiling_height, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :basement_ceiling_height, "Basement Ceiling Height", class: "general-text-label" %>
  <%= ff.number_field :basement_ceiling_height, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :door_size, "Door Size", class: "general-text-label" %>
  <%= ff.number_field :door_size, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :zoning, "Zoning", class: "general-text-label" %>
  <%= ff.text_field :zoning, class: "general-text-field" %>
</div>
<div class="field">
  <label for="tenant_improvements" class="general-text-label">Tenant Improvements <small>(If Applicable)</small></label>
  <%= ff.text_area :tenant_improvements, :rows => "4", class: "general-text-area" %>
</div>
<div class="field">
  <label for="previous_use" class="general-text-label">Previous Use <small>(If Applicable)</small></label>
  <%= ff.text_area :previous_use, :rows => "4", class: "general-text-area" %>
</div>
<div class= "field">
  <%= ff.label :community_board, "Community Board", class: "general-text-label" %>
  <%= ff.text_field :community_board, class: "general-text-field" %>
</div>
<div class="field">
  <%= ff.label :delivery_date, "Delivery Date", class: "general-text-label" %>
  <div class="input-group">
    <span class="input-group-addon"><i class="nklyn-icon-calendar"></i></span>
    <%= ff.text_field :delivery_date, :class => "datepicker general-text-field" %>
</div>
<div class="field">
  <%= ff.label :key_money, "Key Money", class: "general-text-label" %>
  <div class="input-group">
    <span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
    <%= f.text_field :key_money, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
  </div>
</div>
<div class="field">
  <%= ff.label :security_deposit, "Security Deposit", class: "general-text-label" %>
  <div class="input-group">
    <span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
    <%= f.text_field :security_deposit, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
  </div>
</div>
<div class="field">
  <%= ff.label :price_per_sq_ft, "Price Per Sq Ft", class: "general-text-label" %>
  <div class="input-group">
    <span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
    <%= f.text_field :price_per_sq_ft, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
  </div>
</div>
<div class="field">
  <%= ff.label :did_size, "Drive In Doors Size", class: "general-text-label" %>
  <%= ff.number_field :did_size, class: "general-text-field" %>
</div>
<% end %>

Обновлять

  1. Я внес изменения в модель ListingCommercialAttribute и удалил вложенные атрибуты accepts для.

  2. Я изменил f.fields_for на единственное число вместо множественного числа.

  3. Я добавил вложенные атрибуты после родителя (см. ниже)

    def listing_params
       params.require(:listing)
          .permit(:access,
              :address,
              :apartment,
              :cats_ok,
              :cross_streets,
              :dogs_ok,
              :latitude,
              :longitude,
              :amenities,
              :date_available,
              :bathrooms,
              :bedrooms,
              :description,
              :fee,
              :exclusive,
              :featured,
              :rental,
              :residential,
              :landlord_contact,
              :listing_agent_id,
              :sales_agent_id,
              :neighborhood_id,
              :pets,
              :photo,
              :photo_tag,
              :primaryphoto,
              :price,
              :square_feet,
              :station,
              :status,
              :subway_line,
              :term,
              :title,
              :utilities,
              :move_in_cost,
              :owner_pays,
              :private,
              :office_id,
              :full_address,
              :zip,
              :convertible,
              :landlord_llc,
              :pinned,
              :image,
              listing_commercial_attribute_attributes: [
              :gas_pipe_size,
              :amperage,
              :basement_ceiling_height,
              :ceiling_height,
              :door_size,
              :zoning,
              :previous_use,
              :community_board,
              :delivery_date,
              :key_money,
              :security_deposit,
              :price_per_sq_ft,
              :did_size])
    end
    
  4. Вот мои новые действия контроллера:

    def edit
       @listing.attributes = listing_params
    end
    
    def create
       @listing.attributes = listing_params
    
       respond_to do |format|
         if @listing.save
            format.html { redirect_to @listing, notice: 'Listing was successfully created.' }
            format.json { render json: @listing, status: :created, location: @listing }
         else
            format.html { render action: "new", notice: "Correct the mistakes below to create the new listing" }
            format.json { render json: @listing.errors, status: :unprocessable_entity }
        end
      end
    end
    

Но теперь я получаю ошибку NoMethodError in Listings#show. Я создал партиал для коммерческих атрибутов. Разве они не должны быть включены сейчас, когда они находятся в сильных параметрах, или я совершенно неправильно это понимаю?!

Вот частичное:

    Gas Pipe Size: <%= listing_commercial_attributes.gas_pipe_size(@listing) %>
    Amperage: <%= listing_commercial_attribute.amperage(@listing) %>
    Basement Ceiling Height: <%= listing_commercial_attribute.basement_celing_height(@listing) %>
    Ceiling Height: <%= listing_commercial_attribute.ceiling_height(@listing) %>
    Door Size: <%= listing_commercial_attribute.door_size(@listing) %>
    Zoning: <%= listing_commercial_attribute.zoning(@listing) %>
    Build to Suit: <%= listing_commercial_attribute.build_to_suit(@listing) %>
    Previous Use: <%= listing_commercial_attribute.previous_use(@listing) %>
    Community Board: <%= listing_commercial_attribute.community_board(@listing) %>
    Delivery Date: <%= listing_commercial_attribute.delivery_date(@listing) %>
    Key Money: <%= listing_commercial_attribute.key_money(@listing) %>

Обновление №2

Я изменил его на единственное число.

Вот полная ошибка.

Ошибка имени в листингах#show

Показ /Users/Code/app/views/listings/_commercial_attributes.html.erb, где строка № 1 поднята:

неопределенная локальная переменная или метод `listing_commercial_attribute' для #‹#:0x007f86606f6a10> Вы имели в виду? listing_collection_url

  • Размер газовой трубы: ‹%= listing_commercial_attribute.gas_pipe_size(@listing) %>
  • Сила тока: ‹%= listing_commercial_attribute.amperage(@listing) %>
  • Высота потолка в подвале: ‹%= listing_commercial_attribute.basement_celing_height(@listing) %>
  • Высота потолка: ‹%= listing_commercial_attribute.ceiling_height(@listing) %>
  • Размер двери: ‹%= listing_commercial_attribute.door_size(@listing) %>
  • Зонирование: ‹%= listing_commercial_attribute.zoning(@listing) %>

След включения шаблона: app/views/listings/_listing_content_area.html.erb, app/views/listings/show.html.erb.


Обновление №3

  def show
      @my_listing_collections = ListingCollection.with_agent(current_agent).order("created_at DESC")
      @listing_commercial_attributes = ListingCommercialAttribute.find(params[:id])
      @regions = Region.order(name: :asc)
      @listing = Listing.includes(:photos, :likes, :interested_agents).find(params[:id])

      if @listing.private && cannot?(:create, Listing)
        redirect_to listings_path, notice: 'This listing is no longer available'
      else
        agent = Agent.where(id: params[:agent_id]).first
        @page = Listings::ShowView.new(@listing, agent)

        respond_to do |format|
          format.html
        end
      end
    end

Я продолжаю получать эту ошибку:

ActiveRecord::RecordNotFound в ListingsController#show

Не удалось найти ListingCommercialAttribute с идентификатором 5755.

Он ищет коммерческий атрибут с идентификатором 5755, но это идентификатор листинга. Я не уверен, что там передать...


person Mike Wiesenhart    schedule 06.06.2016    source источник
comment
какие новые атрибуты не сохраняются?   -  person Hare Kumar    schedule 07.06.2016
comment
Те, что перечислены выше в частичном. Они не сохраняются в БД, и я сталкиваюсь с этой ошибкой.   -  person Mike Wiesenhart    schedule 07.06.2016


Ответы (1)


  1. Не определяйте accepts_nested_attributes_for на обеих моделях. Только на родительской модели. В противном случае вы столкнетесь с проблемами циклической зависимости. В этом случае родительская модель выглядит как Листинг, поэтому удалите accepts_nested_attributes_for :listing из ListingCommercialAttribute.
  2. Первым аргументом f.fields_for должно быть имя ассоциации, а ваше немного отличается. У вас есть has_one : listing_commerical_attribute, поэтому вы хотите f.fields_for : listing_commerical_attribute.
  3. Строгие параметры должны сначала требовать ваш родительский объект, а затем включать вложенные объекты. Кроме того, вы должны добавить _attributes в конец имени вашего вложенного атрибута.

Итак, для 3:

def listing_params
  params.require(:listing)
        .permit(:id,
                # ...
                listing_commercial_attribute_attributes: [ # Note: _attributes
                  :gas_pipe_size,
                  # ...
                ])
end
  1. В действиях создания/редактирования обязательно установите параметры из метода сильных параметров: @listing.attributes = listing_params.

Подробнее читайте в документации на accepts_nested_attributes_for и Надежные параметры.

person pdobb    schedule 07.06.2016
comment
Я внес предложенные изменения, но все еще сталкиваюсь с некоторыми ошибками. Не могли бы вы взглянуть еще раз. Я ввел обновления выше! Заранее спасибо! @pdobb - person Mike Wiesenhart; 07.06.2016
comment
Вы не показали достаточно ошибки, чтобы я точно знал, в чем проблема. Но опять же у вас есть множественное число, где вы не должны на странице Показать. - person pdobb; 07.06.2016
comment
Похоже, вы еще не передали локальную переменную `listing_commercial_attribute' в свой партиал. - person pdobb; 07.06.2016
comment
Я проработал ошибки, но коммерческие атрибуты по-прежнему не сохраняются в БД. Что я забыл? @pdobb - person Mike Wiesenhart; 07.06.2016
comment
Первое, что нужно попробовать в этом случае, это вставить отладчик/привязку после вызова attributes = в контроллер и сравнить то, что находится в listing_params, с тем, что вы ожидаете, а затем сравнить то, что было установлено в объекте, с тем, что вы видели в listing_params. Например. осмотрите свой объект @listing и связанный с ним объект @listing.listing_commercial_attribute. На этом этапе они должны иметь свои значения. Если нет, то вы можете исключить сохранение как проблему и изучить сильные параметры. Если это так, вы можете больше узнать об ошибках проверки с помощью фактической команды save. - person pdobb; 07.06.2016
comment
Ууууу! Я получил это, и я думаю, что я почти там. Могу я задать вам еще один вопрос? Я не могу понять, что передать в метод контроллера показа, чтобы получить коммерческие атрибуты для отображения на странице показа списков. Я включил обновленный контроллер выше. @pdobb - person Mike Wiesenhart; 09.06.2016