Отношения Rails has_many с предварительно заполненными представлениями

У меня есть довольно простое приложение Rails 4, и я использую вложенные формы Cocoon для управления ассоциацией модели has_many... :through.

class Student < ActiveRecord::Base
  has_many :evaluations
  has_many :assessments, through: :evaluations

  # ... etc
end

class Evaluation < ActiveRecord::Base
  belongs_to :student
  belongs_to :assessment

  # ... etc
end

class Assessment < ActiveRecord::Base
  has_many :evaluations
  has_many :students, through: :evaluations

  accepts_nested_attributes_for :evaluation, reject_if: :all_blank
  # ... etc
end

Когда я использую Cocoon в представлении, я хочу использовать представление New Assessment для предварительного заполнения всех Student записей, чтобы создать новую Evaluation для каждой из них. Я не хочу выполнять какую-то хакерскую логику на стороне контроллера, чтобы вручную добавлять новые записи, так как мне структурировать входящий запрос? С Cocoon я вижу, что запросы имеют некоторый номер в пространстве, где будет идти id (ниже я заменил их на ??).

{"utf8"=>"✓", "authenticity_token"=>"whatever", "assessment"=>{"description"=>"quiz 3", "date(3i)"=>"24", "date(2i)"=>"10", "date(1i)"=>"2015", "assessments_attributes"=>{"??"=>{"student_id"=>"2", "grade" => "A"}, "??"=>{"student_id"=>"1", "grade" => "B"}, "??"=>{"student_id"=>"3", "grade"=>"C"}}, }}, "commit"=>"Create Assessment"}

В исходном коде Coccoon я вижу, что это каким-то образом генерируется, но я не могу понять, как это работает с движком Rails, чтобы превратить это в новую запись без ID.

Какой алгоритм я должен использовать (или правила, которым я должен следовать), чтобы заполнить id выше, чтобы сделать новую запись?


person charlie    schedule 24.10.2015    source источник
comment
Пожалуйста, включите соответствующий контроллер   -  person max    schedule 24.10.2015
comment
@max Контроллер не имеет никакой специальной логики (атм), кроме параметров из белого списка и вызова @assessment.new(assessment_params), а затем @assessment.save   -  person charlie    schedule 24.10.2015
comment
Да, но вы обычно создаете связанные записи в новом действии контроллера — таким образом нам не приходится строить догадки.   -  person max    schedule 24.10.2015
comment
Я хочу избежать создания этих вещей вручную. Если я создаю новые записи через представление (добавляю новые записи для каждого ученика — то, что я хочу автоматизировать), оно структурирует их, как указано выше, и мне не нужна какая-либо специальная логика контроллера, кроме использования new и использования вложенных форм и accepts_nested_attributes_for. Я просто передаю параметры из белого списка методу new, и Rails создает родительский assessment и связанные с ним evaluation объекты.   -  person charlie    schedule 24.10.2015


Ответы (2)


"??"

Никогда в ваших параметрах нет хорошего знака.

С Cocoon я вижу, что запросы имеют некоторый номер в пространстве, где будет идти id

Этот ID — не что иное, как следующий идентификатор в массиве fields_for, который создает Rails. Это не id вашей записи (подробнее см. ниже).


Из вашей настройки, вот что я бы сделал:

#app/models/student.rb
class Student < ActiveRecord::Base
   has_many :evaluations
   has_many :assessments, through: :evaluations
end

#app/models/evaluation.rb
class Evaluation < ActiveRecord::Base
  belongs_to :student
  belongs_to :assessment
end

#app/models/assessment.rb
class Assessment < ActiveRecord::Base
   has_many :evaluations
   has_many :students, through: :evaluations
   accepts_nested_attributes_for :evaluations, reject_if: :all_blank
end

Это позволит вам сделать следующее:

#app/controllers/assessments_controller.rb
class AssessmentsController < ApplicationController
   def new
      @assessment = Assessment.new
      @students = Student.all
      @students.each do
         @assessment.evaluations.build
      end
   end
end

Позволяя вам:

#app/views/assessments/new.html.erb
<%= form_for @assessment do |f| %>
   <%= f.fields_for :evaluations, @students do |e| %>
       <%= e.hidden_field :student_id %>
       <%= e.text_field :grade %>
   <% end %> 
   <%= f.submit %>
<% end %>

Насколько я могу судить, это обеспечит необходимую функциональность.

Помните, что каждый evaluation может соединяться с существующим students, а это означает, что если вы потянете @students = Student.all, он соответственно заполнит fields_for.

Если вы хотите добавить новый students через форму, это немного другая игра.


Кокон

Вы также должны четко понимать роль Cocoon.

Вы кажетесь опытным разработчиком, поэтому я перейду к сути: Cocoon — это внешний интерфейс, а то, что вы спрашиваете, — внутренний.

В частности, Cocoon предназначен для того, чтобы дать вам возможность добавлять в форму ряд fields_for связанных полей. Это обсуждалось в этом Railscast...

введите здесь описание изображения

Технически Cocoon — это всего лишь способ создания новых fields_for записей для формы. Это только требуется, если вы хотите динамически "добавлять" поля (RailsCast расскажет вам больше).

Таким образом, если вы хотите просто иметь «статический» массив ассоциативных полей данных (о чем, я думаю, вы и просите), вы сможете использовать fields_for, представленный как в Max, так и в моих ответах.

person Richard Peck    schedule 24.10.2015
comment
Большое спасибо @rich-peck. Ага, ?? были заменены мной специально для этого вопроса, поэтому в запросе они не проходят :) - person charlie; 24.10.2015
comment
(слишком быстро нажал клавишу ввода) Я знаю, что Cocoon полностью интерфейсный, но я не был уверен, какая логика создала идентификаторы, которые он передал обратно через запрос. Разве они просто не имеют значения, пока они уникальны? Или они должны быть чем-то, что не соответствует уже существующим идентификаторам evaluation? - person charlie; 24.10.2015
comment
Идентификаторы Time.now.to_i ;) - person Richard Peck; 24.10.2015
comment
Я думал, что они могут быть! Это полностью отвечает на мой вопрос. Большое спасибо за ваше время. - person charlie; 24.10.2015
comment
stackoverflow.com/questions/20436526/ - или, по крайней мере, так, как мне нравится это делать - person Richard Peck; 24.10.2015

Благодаря @rich-peck я точно понял, чем хочу заниматься. Я оставляю его ответ принятым, потому что именно так я пришел к своему собственному. :)

assessments/new.html.haml (просто сырой, без причудливого форматирования)

= form_for @assessment do |f|
  = f.fields_for :evaluations do |ff|
    .meaningless-div
      = ff.object.student.name
      = ff.hidden_field :student_id, value: ff.object.student_id
      = ff.label :comment
      = ff.text_field :comment
      %br/

assessments_controller.rb

def new
  @assessment = Assessment.new
  @students = Student.all
  @students.each do |student|
    @assessment.evaluations.build(student: student)
  end
end
person charlie    schedule 28.10.2015