Автоматическое создание объекта ассоциаций has_one и belongs_to

в 15:57, , рубрики: gem, rails, ruby on rails, метки: ,

Если у вас когда либо возникала чудная ситуация, когда вы прописали ассоциации для моделей (has_one и belongs_to), accepted_nested_attributes, но хелпер fields_for не показывает форму то это пост поожет вам раз и навсегда избавится от этого «глюка». Заранее предупреждаю, что пост сугубо для rails разработчиков, и ни для кого никому, кроме как rails разработчикам интересен не будет!

Предусловия

Итак, допустим у нас есть следующие модели:

class Company < ActiveRecord::Base
    belongs_to :location
    has_one :user
    accepts_nested_attributes_for :user
    accepts_nested_attrbiutes_for :location
end

class User < ActiveRecord::Base
    belongs_to :company
end

class Location < ActiveRecord::Base
    has_one :company
end

Нам нужна форма компании с вложенными ресурсами user и location. На первый взгляд может показаться что кода:

form_for :company do |f|
    f.text_field :name
    f.fields_for :user, f.object.user_or_build do |fu|
        fu.text_field :name
    end
    f.fields_for :location, f.object.location_or_build do |fl|
        fl.text_field :address
    end
end

будет достаточно, чтобы показать желаемую форму. Однако это не так. Запустив приложение и открыв браузер вы увидете, что вместо ожидаемых 3-х полей на странице всего одно — это поле name для company (предполагается, что у нас есть проинициализированный объект как то так:

@company = Company.new

).

Что же в этом коде не так?

Проблема кроется в том, что мы не проинициализировали ассоциированные объекты user и location для @company:

@company.build_location # то же что и @company.location = Location.new
@company.build_user # то же что и @company.user = User.new

Если эти условия выполнить до выполнения хелперов fields_for, то форма будет выглядеть как мы и ожидали. Однако писать каждый раз такой код в контроллерах как-то несерьезно, ведь контроллеры должны быть чистыми!

Решение

Избежать данную проблему можно с помощью этого гема, который носит немного неуклюжие название — get_or_build.
Добавляем в Gemfile строку:

    gem 'get_ot_build'

Устанавливаем gems: bundle install, перезапускаем приложение и рефакторим код с хелперами fields_for следующим образом:

    form_for :company do |f|
        f.fields_for :user, :build_association => true do |fu|
            fu.text_field :name
        end
        f.fields_for :user, :build_association => true do |fl|
            fl.text_field :address
        end
    end

После этого код инициализации объектов ассоциаций user и location можно удалить из контроллеров, шаблонов и хелперов (еще где-то, где он есть ;) и уже не нужен).

Буду очень признателен за критику и коментарии, также приветствуются добровольцы для развития гема. Спасибо за внимание!

Автор: ka8725

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js