简体   繁体   English

在Rails中动态创建模型的多个实例

[英]Dynamically create multiple instances of a model in rails

I have two related models "Test" and "Student": 我有两个相关的模型“测试”和“学生”:

class Student< ActiveRecord::Base
   has_many :tests
end

class Test< ActiveRecord::Base
   belongs_to :Student
end

What I'm trying to do is create an unknown number of tests for a student, ie from the student's show.html.erb, user may create multiple tests. 我想做的是为学生创建数量未知的测试,即从学生的show.html.erb中,用户可以创建多个测试。 Also, once a set of tests are created, user can again add any number of tests via the student's show.html.erb in the same way as before. 同样,一旦创建了一组测试,用户就可以再次以与以前相同的方式通过学生的show.html.erb添加任意数量的测试。

Please note that test creation is not part of student creation (Like it is in this railscast). 请注意,测试创建不是学生创建的一部分(就像在栏目中一样)。 Student is created separately and then multiple tests can be added from a button on app/views/students/show.html.erb. 学生是单独创建的,然后可以通过app / views / students / show.html.erb上的按钮添加多个测试。

I'm calling a custom action (customAction1 below) in the students_controller.rb when the button to add tests is clicked. 当单击添加测试的按钮时,我在students_controller.rb中调用一个自定义动作(以下为customAction1)。 What I can think of is to dynamically build and append, say 10 (for a start) instances of tests to a student's instance (in the students_controller.rb's custom action), render a custom view (addtests.html.erb below), then allow user to enter the various input fields for these test instances in that view, and finally when create button is clicked, call another custom action (customAction2 below) in the students_controller.rb to save (only the non-empty) test instances. 我能想到的是动态构建并向学生的实例添加10个(作为开始)测试实例(作为开始)(在students_controller.rb的自定义动作中),呈现自定义视图(下面的addtests.html.erb),然后允许用户在该视图中为这些测试实例输入各种输入字段,最后单击创建按钮时,在students_controller.rb中调用另一个自定义操作(下面的customAction2)以保存(仅非空)测试实例。

Is this the correct (read Rails) way to go about it? 这是正确的(阅读Rails)方法吗? And since I'm new to rails please correct my code that does the aforementioned things: 由于我是Rails的新手,请更正执行上述操作的代码:

students_controller.rb
#called when button to add tests is clicked from students/show.html.erb view:
def customAction1
    @student = Student.find(params[:student])
    10.times {@student.tests.append}
    render 'addtests'
end

addtests.html.erb
# Here I'm unable to show input fields for each of the 10 newly added tests

students_controller.rb
# Called when create button is clicked from addtests.html.erb after inputting fields for some tests
def customAction2
    @student.tests.save  # Is this correct?
    render 'show' # render show.html.erb of this student
end

If this is indeed the right approach, please let me know how to write the addtests.html.erb view. 如果确实是正确的方法,请让我知道如何编写addtests.html.erb视图。

You can still add the tests through the student as an update student not a create and use cocoon gem to help you with the nested forms, I'm thinking of something like this 您仍然可以通过学生添加测试,将其作为更新学生,而不是创建并使用cocoon宝石来帮助您处理嵌套表格,我在想这样的事情

Gemfile 的Gemfile

gem 'cocoon'

Gem link: https://github.com/nathanvda/cocoon 宝石链接: https : //github.com/nathanvda/cocoon

models/student.rb 车型/ student.rb

accepts_nested_attributes_for :tests

students_controller.rb students_controller.rb

def show
   @student = Student.find(params[:id])
   @student.tests.build #you can check here if the student has already tests you can skip this build
end

def student_params
    #add your own student parameters before the tests attributes
    params.require(:student).permit(tests_attributes: [:id, :name, :_destroy]
end

students/show.html.haml 学生/ show.html.haml

= form_for @student do |f|
  = f.fields_for :tests do |builder|
    = render 'tests/test_fields', f: builder
  = link_to_add_fields 'Add Tests', f, :tests

This form will submit to students#update action with the test_attributes only as parameters 此表单仅使用test_attributes作为参数提交给students#update test_attributes操作

tests/_test_fields.html.haml 测试/ _test_fields.html.haml

-#add other test attributes that you might have
= f.input :name
= link_to_remove_association 'Remove Test', f

I ended up watching the revised version of the nested form railscast to solve the problem. 我最终观看了嵌套表单railscast的修订版以解决此问题。 Following are the steps: 步骤如下:

students_controller.rb
#called when button to add tests is clicked from students/show.html.erb view:
def addTests
    @student = Student.find(params[:student])
    render 'addtests'
end

addtests.html.erb    
    <%= form_for(@student, url: {action: "realAddTests"}, method: :post) do |student_form| %>
      <% test1 = Test.new %> #to show fields for one test by default
      <%= student_form.fields_for(:tests, test1) do |f| %>
        <%= render('test_fields', f: f) %>
      <% end %>
      <br />
      <%= link_to_add_fields 'ADD MORE', student_form, :tests %><br /><br />
      <%= student_form.submit "Save", class: "btn btn-primary" %>
    <% end %>

_test_fields.html.erb  #partial used above
<fieldset>
    <%= f.label "Name" %>
    <%= f.number_field :name %>

    <%= f.label :date %>
    <%= f.date_field :date %>

    <%= f.label :time %>
    <%= f.time_field :time %>
</fieldset>

students_controller.rb
# Called when save button is clicked in addtests.html.erb after inputting fields for some tests  
def realAddTests
new_tests_hash = params[:student][:tests_attributes]
            new_tests_hash.each do |key, value|
                new_test_attributes = value.permit(:name, :date, :time)
                @test = Test.new(new_test_attributes)
            end
end

Modify app\\assets\\javascripts\\tests.coffee and app\\helpers\\application_helper exactly like it's specified in the railscast: 完全按照在railscast中指定的方式修改app \\ assets \\ javascripts \\ tests.coffee和app \\ helpers \\ application_helper:

app\\assets\\javascripts\\tests.coffee: 应用程序\\资产\\ JavaScript的\\ tests.coffee:

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

app\\helpers\\application_helper.rb: 应用程序\\佣工\\ application_helper.rb:

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

Above is the complete set of steps that worked for me and I can create and save multiple tests for a student in one go. 上面是对我有用的完整步骤,我可以一次创建并保存一个学生的多个测试。

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

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