Динамически создавать несколько экземпляров модели в рельсах

У меня есть две родственные модели "Тест" и "Студент":

class Student< ActiveRecord::Base
   has_many :tests
end

class Test< ActiveRecord::Base
   belongs_to :Student
end

То, что я пытаюсь сделать, это создать неизвестное количество тестов для учащегося, т.е. из студенческого show.html.erb пользователь может создать несколько тестов. Кроме того, как только набор тестов создан, пользователь может снова добавить любое количество тестов через show.html.erb учащегося, как и раньше.

Обратите внимание, что создание тестов не является частью творчества учащихся (как в этом рельсовая трансляция). Студент создается отдельно, а затем можно добавить несколько тестов с помощью кнопки в app/views/students/show.html.erb.

Я вызываю пользовательское действие (customAction1 ниже) в файле student_controller.rb, когда нажимается кнопка для добавления тестов. Что я могу придумать, так это динамически создавать и добавлять, скажем, 10 (для начала) экземпляров тестов к экземпляру учащегося (в пользовательском действии student_controller.rb), отображать пользовательское представление (addtests.html.erb ниже), затем разрешить пользователю вводить различные поля ввода для этих тестовых экземпляров в этом представлении, и, наконец, когда нажата кнопка создания, вызвать другое пользовательское действие (customAction2 ниже) в student_controller.rb, чтобы сохранить (только непустые) тестовые экземпляры.

Это правильный (читай 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

Если это действительно правильный подход, дайте мне знать, как написать представление addtests.html.erb.


person AarCee    schedule 21.08.2015    source источник


Ответы (2)


Вы по-прежнему можете добавлять тесты через студента в качестве студента обновления, а не создавать и использовать cocoon gem, чтобы помочь вам с вложенными формами, я думаю о чем-то вроде этого

Gemfile

gem 'cocoon'

Ссылка на Gem: https://github.com/nathanvda/cocoon

модели/студент.рб

accepts_nested_attributes_for :tests

student_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

студенты/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

Эта форма отправит действие students#update с параметрами test_attributes только

тесты/_test_fields.html.haml

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

В итоге я посмотрел исправленную версию вложенной формы railscast, чтобы решить проблему. Ниже приведены шаги:

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

Измените app\assets\javascripts\tests.coffee и app\helpers\application_helper точно так, как указано в railscast:

приложение\активы\javascripts\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()

приложение\помощники\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

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

person AarCee    schedule 02.09.2015