Rails4建议:如何从单一视图在模型中创建多个记录?

时间:2015-11-13 19:27:49

标签: ruby-on-rails ruby-on-rails-4 nested-forms

几天来一直在努力解决这个问题。我有3个模型书籍,儿童和雇用。我已经为招聘创建了一个视图,允许用户选择2本书和一个孩子,我想要做的是插入两行以将其反映到“雇佣者”中。表。我有一些JS用他们需要的值填充隐藏字段。现在,I don't think nested attributes is the way to go,因为我试图直接插入到连接表中。

所以,我现在尝试的是以下内容:

雇用/ _form.html.erb

<%= form_for(@hire) do |f| %>    
 <% 2.times do %>
    <%= f.hidden_field :child_id %>
    <%= f.hidden_field :book_id %>
 <% end %>
<%= f.submit 'Take me home' %>
<% end %>

然后我要做的就是贯穿“创造”#39;在我的控制器中运行两次,从而在“雇佣”中创建两行。模型。像这样:

hires_controller.rb

def create        
hire_params.each do |hire_params|
@hire = Hire.new(hire_params)
end
end 

这完全是错误的方式吗?我正在寻找关于正确方式的建议吗?如果这样可行,那么格式化create语句的最佳方法是什么?

** 修改 **

我有3个型号。每个孩子可以有2本书。这些是我的协会:

class Child < ActiveRecord::Base
 has_many :hires
 has_many :books, through: :hires
end

class Hire < ActiveRecord::Base
 belongs_to :book
 belongs_to :child
 accepts_nested_attributes_for :book
 accepts_nested_attributes_for :child
end

class Book < ActiveRecord::Base
  has_many :hires
  has_many :children, through: :hires
  belongs_to :genres
end

雇用/ new.html.erb

<div class="form-inline">
  <div class="form-group">
<h1><label for="genre_genre_id">Pick Book 1:

  <%=collection_select(:genre1, :genre_id, @genres.all, :id, :Name, {prompt: true}, {:class => "form-control dropdown"})%></label></h1>
  </div>
  </div>

<div id="book1-carousel" class="owl-carousel owl-theme">
  <% @books.each do |book| %>
      <div data-id = "<%= book.id %>" class="tapbook1 tiles <% @genres.each do |g|%> <% if g.id == book.Genre_id %> book1genre<%=g.id %> <% end end%> <%=  %>"><a class="item link"><% if book.bookimg.exists? %><%= image_tag book.bookimg.url(:small), :class => "lazyOwl", :data => { :src => book.bookimg.url(:small)}%> <%end%></br><p class="tile_title" ><%= book.Title %></p></a></div>
  <% end %>
</div>



<div class="form-inline">
  <div class="form-group">
    <h1><label for="genre_genre_id">Pick Book 2:

      <%=collection_select(:genre2, :genre_id, @genres.all, :Name, :Name, {prompt: true}, {:class => "form-control dropdown"})%></label></h1>
  </div>
</div>

<div id="book2-carousel" class="owl-carousel owl-theme">
  <% @books.each do |book| %>
      <div data-id = "<%= book.id %>" id="<%= book.id %>" class="tapbook2 tiles <% @genres.each do |g|%> <% if g.id == book.Genre_id %> book2genre<%=g.id %> <% end end%> <%=  %>"><a class="item link"><% if book.bookimg.exists? %><%= image_tag book.bookimg.url(:small) , :class => "lazyOwl", :data => { :src => book.bookimg.url(:small)}%> <%end%></br> <p class="tile_title"><%= book.Title %></p></a></div>
  <% end %>
</div>

 <h1 class="child_heading1" >Now choose your name:</h1>

<div id="children-carousel" class="owl-carousel owl-theme">
  <% @children.each do |child| %>
      <div data-id = "<%= child.id %>" class="tapchild tiles"><a class="item link"><% if child.childimg.exists? %><%= image_tag child.childimg.url(:small), :class => "lazyOwl", :data => { :src => child.childimg.url(:small)} %> <%end%></br> <p class="tile_title"><%= child.nickname %></p></a></div>
  <% end %>
</div>




<%= render 'form' %>

和coffeescript:

hires.coffee

$(document).on 'ready page:load', ->

  book1carousel = $("#book1-carousel")
  book2carousel = $('#book2-carousel')


  book1carousel.owlCarousel items: 5, lazyLoad : true
  book2carousel .owlCarousel items: 5, lazyLoad : true
  $('#children-carousel').owlCarousel items: 5, lazyLoad : true

  book1clickcounter = 0
  book2clickcounter = 0
  childclickcounter = 0

  book1selection = 0
  book2selection = 0



  $('.tapbook1').on 'click', (event) ->
    $this = $(this)
    book1id = $this.data('id')
    book1selection = book1id


    if $this.hasClass('bookclicked')
      $this.removeAttr('style').removeClass 'bookclicked'
      book1clickcounter = 0
      $('#hire_book_id').val("");
      book1selection = 0
    else if book1clickcounter == 1
      alert 'Choose one book from this row'
    else if book1selection == book2selection
      alert "You've already picked this book"
    else
      $('#hire_book_id').val(book1id);
      $this.css('border-color', 'blue').addClass 'bookclicked'
      book1clickcounter = 1

    return

  $('.tapbook2').on 'click', (event) ->
    $this = $(this)
    book2id = $this.data('id')
    book2selection = book2id

    if $this.hasClass('book2clicked')
      $this.removeAttr('style').removeClass 'book2clicked'
      book2clickcounter = 0
      book1selection = 0
    else if book2clickcounter == 1
      alert 'Choose one book from this row'
    else if book1selection == book2selection
      alert "You've already picked this book"
    else

      $this.css('border-color', 'blue').addClass 'book2clicked'
      book2clickcounter = 1

    return


  $('.tapchild').on 'click', (event) ->
   $this = $(this)
   childid = $this.data('id')
   if $this.hasClass('childclicked')
     $this.removeAttr('style').removeClass 'childclicked'
     childclickcounter = 0
     $('#hire_child_id').val("");
   else if childclickcounter == 1
     alert 'Choose one child from this row'
   else
     $this.css('border-color', 'blue').addClass 'childclicked'
     childclickcounter = 1
     $('#hire_child_id').val(childid);
   return

  jQuery ($) ->
  $('td[data-link]').click ->
    window.location = @dataset.link
    return
  return


return

1 个答案:

答案 0 :(得分:0)

我对此的处理方式是什么称为表单对象,一个类似于模型的类,但只存在于处理多个对象的创建。它提供了精细控制,但代价是重复验证。在我看来(以及许多其他人),在大多数情况下,它比嵌套属性更好。

这是一个例子。请注意,我不知道您的应用程序的功能,并且我没有仔细查看您的关联,以便在此示例中使其准确无误。希望你能得到一般的想法:

class HireWithBookAndChild

  include ActiveModel::Model

  attr_accessor :child_1_id, :child_2_id, :book_id

  validates :child_1_id, presence: true
  validates :child_2_id, presence: true
  validates :book_id,  presence: true

  def save
    if valid?
      @hire = Hire.new(hire_params)
      @child_1 = @hire.child.create(id: params[:child_1_id])
      @child_2 = @hire.child.create(id: params[:child_2_id])
      @book = @hire.book.create(id: params[:book_id])
    end
  end
end

通过包含AR :: Model,您可以获得验证和可以创建表单的对象。您甚至可以进入i18n文件并为此对象配置验证错误消息。与ActiveRecord模型一样,save方法会自动包装在事务中,因此如果其中一个对象无法持久存在,您将无法获得孤立对象。

您的控制器将如下所示:

class HireWithBookAndChildController < ApplicationController

 def new
   @hire = HireWithBookAndChild.new
 end

 def create
   @hire = HireWithBookAndChild.new(form_params)
   if @hire.save
     flash['success'] = "Yay"
     redirect_to somewhere
   else
     render 'new'
   end
 end

 private

 def form_params
   params.require(:hire_with_book_and_child).permit(:child_1_id, :child_2_id, :book_id)
 end

您的表单将如下所示:

form_for @hire do |f|

  f.hidden_field :child_1_id
  ...

  f.submit
end

你会立即注意到一切都是平的,你不必乱用于field_for和嵌套的嵌套参数,如下所示:

params[:child][:id]

您会发现表单对象使您的代码更容易理解。如果您有需要创建的子项,书籍和雇员的不同组合,只需为每个组合创建一个自定义表单对象。

更新

在这种情况下可能更简单的解决方案是提取服务对象:

class TwoHiresWithChildAndBook < Struct.new(:hire_params)

  def generate
    2.times do
      Hire.create!(hire_params)
    end
  end
end

从您的控制器:

class HiresController

  def create
    generator = HireWitHChildAndBook.new(hire_params)
    if generator.generate
      *some behavior*
    else
      render :new
    end
  end
end

这包含了如何在一个地方创建雇佣的知识。我的回答中有更详细的内容:Rails 4 Create Associated Object on Save