IT박스

Rails 3.2 대량 할당을 통해 여러 새 항목을 제출하는 방법

itboxs 2020. 12. 25. 09:05
반응형

Rails 3.2 대량 할당을 통해 여러 새 항목을 제출하는 방법


꽤 표준적인 사용 사례가 있습니다. 부모 개체와 자식 개체 목록이 있습니다. 한 번에 모든 하위 항목을 테이블의 행으로 편집 할 수있는 테이블 형식을 원합니다. 또한 하나 이상의 을 삽입 하고 제출시 새 레코드로 만들 수 있기를 원합니다 .

나는를 사용하는 경우 fields_for가-많은 관련 중첩 된 레코드에 대한 하위 형태의 시리즈를 렌더링, 레일 예를 들어, 필드 이름을 생성 parent[children_attributes][0][fieldname], parent[children_attributes][1][fieldname]등등.

이로 인해 Rack은 다음과 같은 params 해시를 구문 분석합니다.

{ "parent" => { 
    "children" => {
      "0" => { ... },
      "1" => { ... } } }

전달 된 때 (유엔 지속) 개체를 동일 fields_for필드 이름을 생성합니다 외모와 같은 것을 :

parent[children_attributes][][fieldname]

[]색인이없는 것을 참고하십시오 .

이것은 할 수 있는 필드가 포함와 같은 형식으로 게시 [0], [1]등 랙 혼란과 인상됩니다 있기 때문에

TypeError: expected Array (got Rack::Utils::KeySpaceConstrainedParams)

"OK"라고 생각합니다. " 모든 필드가 []양식 대신 양식을 사용하도록 할 것입니다 [index]. 그러나이 fields_for작업을 일관되게 수행 하는 방법을 알 수 없습니다 . 명시적인 필드 이름 접두사를 제공하더라도 및 개체 :

fields_for 'parent[children_attributes][]', child do |f| ...

너무 오래로 child유지됩니다 자동으로 그들이 예를 들면하게되도록 fieldName에 수정됩니다 parent[children_attributes][0][fieldname]새로운 기록을 위해 fieldName에를 떠나있는 동안, parent[children_attributes][][fieldname]. 다시 한번, Rack barfs.

나는 헤매고있다. 표준 Rails 헬퍼를 사용 하여 기존 레코드와 함께 fields_for여러 개의 레코드 를 제출 하고, 매개 변수에서 배열로 구문 분석하고, ID가없는 모든 레코드를 DB에서 새 레코드로 생성하도록하려면 어떻게해야합니까? 운이 좋지 않고 모든 필드 이름을 수동으로 생성해야합니까?


다른 사람들이 언급했듯이 []에 새 레코드에 대한 키를 포함해야합니다. 그렇지 않으면 해시를 배열 유형과 혼합하기 때문입니다. child_indexfields_for 옵션으로이를 설정할 수 있습니다 .

f.fields_for :items, Item.new, child_index: "NEW_ITEM" # ...

나는 일반적으로 object_id여러 새 항목이있는 경우 고유한지 확인하기 위해 대신을 사용하여 수행 합니다.

item = Item.new
f.fields_for :items, item, child_index: item.object_id # ...

다음은이를 수행하는 추상 도우미 메서드입니다. 이것은 item_fields렌더링 할 이름이있는 부분이 있다고 가정합니다 .

def link_to_add_fields(name, f, association)
  new_object = f.object.send(association).klass.new
  id = new_object.object_id
  fields = f.fields_for(association, new_object, child_index: id) do |builder|
    render(association.to_s.singularize + "_fields", f: builder)
  end
  link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end

이렇게 사용할 수 있습니다. 인수는 링크 이름, 상위 양식 작성기 및 상위 모델에 대한 연관 이름입니다.

<%= link_to_add_fields "Add Item", f, :items %>

그리고 여기에 해당 링크의 클릭 이벤트를 수신하고 필드를 삽입하고 개체 ID를 현재 시간으로 업데이트하여 고유 키를 제공하는 CoffeeScript가 있습니다.

jQuery ->
  $('form').on 'click', '.add_fields', (event) ->
    time = new Date().getTime()
    regexp = new RegExp($(this).data('id'), 'g')
    $(this).before($(this).data('fields').replace(regexp, time))
    event.preventDefault()

이 코드는 유료 구독이 필요한 이 RailsCasts Pro 에피소드 에서 가져온 것입니다 . 그러나 GitHub에서 무료로 사용할 수있는 전체 작동 예제 가 있습니다 .

업데이트 :child_index 자리 표시자를 삽입하는 것이 항상 필요한 것은 아니라는 점을 지적하고 싶습니다 . JavaScript를 사용하여 새 레코드를 동적으로 삽입하지 않으려면 미리 빌드 할 수 있습니다.

def new
  @project = Project.new
  3.times { @project.items.build }
end

<%= f.fields_for :items do |builder| %>

Rails는 새 레코드에 대한 인덱스를 자동으로 삽입하므로 제대로 작동합니다.


그래서 가장 자주 본 솔루션이 만족스럽지 않았습니다. 서버 나 클라이언트 측 JS에서 새 요소에 대한 의사 인덱스를 생성하는 것이 었습니다. 특히 Rails / Rack이 []인덱스로 모두 빈 괄호 ( )를 사용하는 한 항목 목록을 완벽하게 구문 분석 할 수 있다는 사실을 감안할 때 이것은 kludge처럼 느껴집니다 . 다음은 내가 작성한 코드의 근사치입니다.

# note that this is NOT f.fields_for.
fields_for 'parent[children_attributes][]', child, index: nil do |f|
  f.label :name
  f.text_field :name
  # ...
end

옵션 []함께 필드 이름 접두사를으로 끝내면 index: nilRails 인덱스 생성이 비활성화되므로 지속되는 객체를 제공하려고합니다. 이 스 니펫은 새 개체와 저장된 개체 모두에서 작동합니다. 결과 양식 매개 변수 []일관되게를 사용하기 때문에 에서 배열로 구문 분석됩니다 params.

params[:parent][:children_attributes] # => [{"name" => "..."}, {...}]

The Parent#children_attributes= method generated by accepts_nested_attributes_for :children deals with this array just fine, updating changed records, adding new ones (ones lacking an "id" key), and removing the ones with the "_destroy" key set.

I'm still bothered that Rails makes this so difficult, and that I had to revert to a hardcoded field name prefix string instead of using e.g. f.fields_for :children, index: nil. For the record, even doing the following:

f.fields_for :children, index: nil, child_index: nil do |f| ...

...fails to disable field index generation.

I'm considering writing a Rails patch to make this easier, but I don't know if enough people care or if it would even be accepted.

EDIT: User @Macario has clued me in to why Rails prefers explicit indices in field names: once you get into three layers of nested models, there needs to be a way to discriminate which second-level model a third-level attribute belongs to.


The common solution is to add a placeholder into [], and replace it with a unique number on inserting the snippet to the form. Timestamp works most of the time.


Maybe you should just cheat. Put the new records in a different faux attribute that is a decorator for the actual one.

parent[children_attributes][0][fieldname]
parent[new_children_attributes][][fieldname]

It's not pretty, but it should work. It might take some extra effort to support round-trips to the form for validation errors.


I've came across this user case in all my last proyects, and I expect this to continue, as julian7 pointed, it is necesary to provide a unique id inside the []. In my opinion this is better done via js. I've been dragging and improving a jquery plugin for dealing with this situations. It works with existing records and for adding new records but expects a certain markup and it degrades gracefully, heres the code and an example:

https://gist.github.com/3096634

Caveats for using the plugin:

  1. The fields_for call should be wrapped in a <fieldset> with data-association attribute equal to the pluralized name of the model, and a class 'nested_models'.

  2. an object should be built in the view just before calling fields_for.

  3. the object fields perse should be wrapped in a <fieldset> with class "new" but only if the record is new (cant remember if I removed this requirement).

  4. A checkbox for the '_destroy' attribute inside a label must exist, the plugin will use the label text to create a destroy link.

  5. A link with class 'add_record' should exist within the fieldset.nested_models but outside the fieldset enclosing the model fields.

Appart from this nuisances its been working wonders for me.
After checking the gist this requirements must be clearer. Please let me know if you improve on the code or if you use it :).
BTW, I was inspired by Ryan Bates first nested models screencast.


long post deleted

Ryan has an episode on this: http://railscasts.com/episodes/196-nested-model-form-revised

It looks like you need to generate the unique index manually. Ryan uses the object_id for this.


I think you can make it work by including the id of the record as a hidden field


There is a gem called cocoon for doing this, I would go for a leaner mor DIY aproach but it was specifically built for this cases.

ReferenceURL : https://stackoverflow.com/questions/11445831/how-to-submit-multiple-new-items-via-rails-3-2-mass-assignment

반응형