简体   繁体   English

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

[英]Rails4 advice: How should I create multiple records in a model from single view?

Been struggling with this query for a few days. 几天来一直在努力处理此查询。 I have 3 models Books, Children and Hires. 我有3种型号的书籍,儿童和雇用作品。 I have created a view for hires which allows a user to select 2 books and a single child and what i'm looking to do is insert two rows to reflect this into the 'hires' table. 我已经为员工创建了一个视图,该视图允许用户选择2本书和一个孩子,而我想要做的是插入两行以将其反映到“员工”表中。 I have some JS that populates the hidden fields with the values that they require. 我有一些JS,用它们需要的值填充隐藏字段。 Now, I don't think nested attributes is the way to go , because i'm trying to insert directly into the joining table. 现在, 我不认为嵌套属性是解决的办法 ,因为我正试图直接插入联接表中。

So, what i'm trying now is the following: 因此,我现在正在尝试以下操作:

hires/_form.html.erb 员工/_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 %>

and then what I want to do is to run through the 'create' function twice in my controller and thus create two rows in the 'hires' model. 然后我想做的是在控制器中两次运行'create'函数,从而在'hires'模型中创建两行。 Something like this: 像这样:

hires_controller.rb hires_controller.rb

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

Is this completely the wrong way to go? 这是完全错误的方法吗? I'm looking for advice on the right way to do this? 我正在寻找正确方法的建议? If this will work, what's the best way to format the create statement? 如果可行,格式化create语句的最佳方法是什么?

** Edit ** ** 编辑 **

I have 3 models. 我有3个模型。 Each Child can have 2 books. 每个孩子可以拥有2本书。 These are my associations: 这些是我的协会:

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

hires/new.html.erb 雇用/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' %>

and the 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

My approach to this would be what's called a form object, a class that acts like a model but exists only to handle the creation of multiple objects. 我的解决方法是所谓的表单对象,它是一个类似于模型的类,但仅用于处理多个对象的创建。 It provides granular control, but at the expense of duplicating validations. 它提供了精细的控制,但以重复验证为代价。 In my opinion (and that of many others), it's a much better option than nested attributes in most cases. 我认为(以及许多其他观点),在大多数情况下,这是比嵌套属性更好的选择。

Here's an example. 这是一个例子。 Note that I don't have any idea what your application does, and I didn't look at your associations close enough to make them accurate in this example. 请注意,我不知道您的应用程序会做什么,并且在此示例中,我没有足够仔细地查看您的关联以使其准确。 Hopefully you'll get the general idea: 希望您能得到大致的想法:

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

By including AR::Model, you get validations and an object you can create a form with. 通过包含AR :: Model,您可以获得验证和可以用来创建表单的对象。 You can even go into your i18n file and configure the validation errors messages for this object. 您甚至可以进入i18n文件,并为此对象配置验证错误消息。 Like an ActiveRecord model, the save method is automatically wrapped in a transaction so you won't have orphaned objects if one of them fails to persist. 就像ActiveRecord模型一样,save方法自动包装在事务中,因此如果其中一个对象无法持久保存,则不会有孤立对象。

Your controller will look like this: 您的控制器将如下所示:

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

end 结束

Your form will look like this: 您的表单将如下所示:

form_for @hire do |f|

  f.hidden_field :child_1_id
  ...

  f.submit
end

You'll notice right away that everything is flat, and you aren't having to mess with fields_for and nested nested parameters like this: 您会立即注意到所有内容都是平坦的,并且不必像下面这样混淆field_for和嵌套的嵌套参数:

params[:child][:id]

You'll find that form objects make your code much easier to understand. 您会发现表单对象使您的代码更容易理解。 If you have different combinations of children, books and hires that you need to create, just make a custom form object for each one. 如果您需要创建不同的孩子,书籍和员工组合,只需为每个对象创建一个自定义表单对象。

Update 更新资料

A solution that might be more simple in this case is to extract a service object: 在这种情况下,可能更简单的解决方案是提取服务对象:

class TwoHiresWithChildAndBook < Struct.new(:hire_params)

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

And from your controller: 从您的控制器:

class HiresController

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

This encapulates the knowledge of how to create a hire in one place. 这概括了如何在一个地方创建员工的知识。 There's more detail in my answer here: Rails 4 Create Associated Object on Save 我的答案在这里有更多详细信息: Rails 4在保存时创建关联对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM