Rails 4 - Порядок has_many через вложенные атрибуты в форме

tl, dr: как я могу гарантировать, что порядку моих has_many с помощью вложенных атрибутов со значением атрибута, установленным в сборке, всегда назначается один и тот же номер хэш-ключа вложенных параметров (0, 1, и т. д.) и всегда появляются в форме в одном и том же порядке?

Надеюсь, я смогу описать это так, чтобы это имело смысл. У меня есть небольшой прототип приложения, которое имитирует простой банковский перевод между двумя учетными записями, исходной учетной записью и целевой учетной записью. У меня есть класс Transfer, класс Account и класс TransferAccounts, который является сквозным соединением для ассоциации многие_то_многие между Transfer и Account.

Вот действие new в TransfersController:

def new
  @transfer = Transfer.new
  @transfer.transfer_accounts.build(account_transfer_role: 'source').build_account
  @transfer.transfer_accounts.build(account_transfer_role: 'destination').build_account
  bank_selections
  account_selections
end  

И сильные параметры:

def transfer_params
  params.require(:transfer).
    permit(:name, :description,
           transfer_accounts_attributes:
             [:id, :account_id, :account_transfer_role,
              account_attributes:
                [:id, :bank_id, :name, :description, :user_name,
                 :password, :routing_number, :account_number
                ]
             ])
end

Итак, два transfer_accounts, связанных с transfer, имеют атрибут account_transfer_role, причем один из них имеет значение источник, а другой - назначение.

Теперь при заполнении формы и отправке параметры в консоли выглядят следующим образом:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxxxxxxxxxxxxxxxxx==",
"transfer"=>{"name"=>"Test Transfer 2", "description"=>"Second test transfer",
"transfer_accounts_attributes"=>{"0"=>{"account_transfer_role"=>"source", "account_id"=>"1",
"account_attributes"=>{"bank_id"=>"1", "name"=>"George's Checking",
"description"=>"George's personal checking account", "user_name"=>"georgeckg",
"password"=>"[FILTERED]", "account_number"=>"111111111", "routing_number"=>"101010101",
"id"=>"3"}, "id"=>"3"}, "1"=>{"account_transfer_role"=>"destination", "account_id"=>"2",
"account_attributes"=>{"bank_id"=>"2", "name"=>"George's Savings",
"description"=>"George's personal savings account", "user_name"=>"georgesav",
"password"=>"[FILTERED]", "account_number"=>"111101111", "routing_number"=>"100100100",
"id"=>"4"}, "id"=>"4"}}}, "commit"=>"Update Transfer", "id"=>"2"}

Как видите, каждый transfer_account в хэше transfer_account_attributes имеет ключ идентификатора, либо 0, либо 1 (например, ..."0"=>{"account_transfer_role"=>"source"...). Теперь я работал в предположении (которое, как я думал, может снова укусить меня, и оно так и есть), что из-за порядка, в котором они построены в действии new, source transfer_account всегда будет иметь ключ идентификатора 0, а место назначения transfer_account всегда будет иметь ключ идентификатора 1, что побудило меня использовать эти ключи идентификатора в другом месте в контроллер как если бы 0 представлял источник, а 1 представлял назначение.

И все, казалось, работало нормально, пока я не попробовал разные варианты создания новых или использования существующих учетных записей, создания новых или редактирования существующих переводов, когда внезапно появляется форма с местом назначения, указанным первым и источник второй, чего раньше не было, в результате чего записи теперь имеют место назначения, связанное с 0 и source, связанный с 1, нарушая код в контроллере, упомянутом выше.

Чтобы было понятнее, вот форма:

#transfer_form
  = simple_form_for @transfer do |t|
    .form-inputs

      = t.input :name, label: 'Transfer Name'
      = t.input :description, required: false, label: 'Transfer Description'

      = t.simple_fields_for :transfer_accounts do |ta|

        - role = ta.object.account_transfer_role.titleize
        = ta.input :account_transfer_role, as: :hidden

        = ta.input :account_id, collection:    @valid_accounts,
                                include_blank: 'Select account...',
                                label:         "#{ role } Account",
                                error:         'Account selection is required.'

        .account_fields{id: "#{ role.downcase }_account_fields"}

          = ta.simple_fields_for :account do |a|

            = a.input :bank_id,        collection:    @valid_banks,
                                       include_blank: 'Select bank...',
                                       label:         "#{ role } Bank",
                                       error:         'Bank selection is required.',
                                       class:         "#{ role.downcase }_account_input_field"

            = a.input :name,           label:    "#{ role } Account Name",
                                       class:    "#{ role.downcase }_account_input_field"

            = a.input :description,    required: false,
                                       label:    "#{ role } Account Description",
                                       class:    "#{ role.downcase }_account_input_field"

            = a.input :user_name,      label:    "#{ role } Account User Name",
                                       class:    "#{ role.downcase }_account_input_field"

            = a.input :password,       label:    "#{ role } Account Password",
                                       class:    "#{ role.downcase }_account_input_field"

            = a.input :account_number, label:    "#{ role } Account Number",
                                       class:    "#{ role.downcase }_account_input_field"

            = a.input :routing_number, label:    "#{ role } Account Routing Number",
                                       class:    "#{ role.downcase }_account_input_field"

    = t.submit

Как обеспечить, чтобы источник всегда был первым и, таким образом, всегда был связан с ключом идентификатора 0 и < strong> пункт назначения всегда второй, всегда связанный с ключом идентификатора 1?


person eggroll    schedule 11.11.2015    source источник


Ответы (1)


Ответ кажется таким же простым, как изменить строку ассоциации :transfer_accounts в моей модели Transfer:

has_many  :transfer_accounts, inverse_of: :transfer

к этому:

has_many  :transfer_accounts, -> { order('account_transfer_role DESC') }, inverse_of: :transfer

Если кто-то считает, что он не делает то, что я думаю, сообщите мне, потому что на данный момент он, похоже, решает мою проблему.

person eggroll    schedule 12.11.2015
comment
Здесь вы просто просите базу данных вернуть вам transfer_accounts в алфавитном порядке account_transfer_role. Это плохо, потому что это влияет на все ваши запросы к transfer_accounts, в то время как вам нужно только отображать их в указанном порядке. - person muichkine; 23.04.2016