Dazt Dazt - 1 year ago 123
Ruby Question

has_many through access join table attribute in form

I have the following models:

class RandomExam < ActiveRecord::Base
has_many :random_exam_sections
has_many :sections, :through => :random_exam_sections

class Section < ActiveRecord::Base
has_many :random_exam_sections
has_many :random_exams, :through => :random_exam_sections

class RandomExamSection < ActiveRecord::Base
belongs_to :random_exam
belongs_to :section

The idea is to have certain configurations to create random exams, so this tables help to select which sections do you need and then also select the number of questions per section, here are the attributes of each table:

RandomExam: name(string), created_at(datetime), updated_at(datetime)

Section: name(string), created_at(datetime), updated_at(datetime)

RandomExamSection: random_exam_id(integer), section_id(integer), questions_number(integer)

As you can see the number of questions per section attribute is inside the RandomExamSections table and I want to access it in a form that is displayed from the RandomExam controller, here is my form:

<%= form_for (@random_exam) do |f| %>
<div class="row">

<div class="input-field col s12">
<%= f.label :name, 'Name' %>
<%= f.text_field :name, placeholder: 'Enter the name of the configuration' %>


<% @sections.each do |section| %>

<div class="row <%= dom_id(section) %>">
<div class="col s4">
<%= check_box_tag 'random_exam[section_ids][]', section.id,
@random_exam.section_ids.include?(section.id), id: dom_id(section), class: "section-checkbox #{dom_id(section)}" %>
<%= label_tag dom_id(section), (raw sanitize section.name, tags: %w(h2 p strong em a br b i small u ul ol li blockquote), attributes: %w(id class href)),
class: "name #{dom_id(section)}" %>
</div class="col s4">
<%= text_field_tag "random_exam[random_questions_numbers][#{section.id}][]", nil,
:placeholder => 'Enter the number of questions' %>

<% end %>

<div class="form-group">
<%= f.submit class: "btn waves-effect waves-light green" %>

<% end %>

My controller:

def create
@random_exam = RandomExam.new(random_exam_params)
if @random_exam.save
flash[:success] = 'Random configuration created successfully'
redirect_to @random_exam
flash.now[:danger] = @random_exam.errors.full_messages.to_sentence
render 'new'

def assign_random_questions_number
if params[:random_exam][:'random_questions_numbers'] == nil

params[:random_exam][:'section_ids'].each do |key, value|
record = RandomExamSection.search_ids(@random_exam.id, key)

record.each do |random_exam_section_record|
number_of_questions = params[:random_exam][:'random_questions_numbers'][key].first.to_i
random_exam_section_record.update(questions_number: number_of_questions)


I'm getting a TypeError:
TypeError: nil is not a symbol nor a string
when I update the record in the method

This error even appears when I run this on the console

random = RandomExamSection.first
random.update(questions_number: 10)

Or when I run:

random = RandomExamSection.first
random.questions_number = 10


I ended up deleting the association in RandomExamSection and recreating it inside 'assign_random_questions_number' with the questions_number


Answer Source

If you use nested_attributes you can do something like this:

<h4>Selected exams</h4>
<%= f.fields_for :random_exam_sections do |b| %>
  <%= b.hidden_field :section_id %>
  <%= b.label :selected, b.object.section.name %>
  <%= b.check_box :selected, { checked: !b.object.id.blank? } %>
  <%= b.label :question_numbers %>
  <%= b.text_field :questions_number %>
 <% end %>

class RandomExam < ApplicationRecord
  has_many :random_exam_sections, inverse_of: :random_exam
  has_many :sections, :through => :random_exam_sections

  accepts_nested_attributes_for :random_exam_sections, reject_if: :is_not_selected

  def is_not_selected(attr)
    attr["selected"] == '0'

# RandomExam
class RandomExamSection < ApplicationRecord
  belongs_to :random_exam
  belongs_to :section

  attr_accessor :selected

# Controller
# GET /random_exams/new
  def new
    @random_exam = RandomExam.new
    @random_exam.random_exam_sections.build(Section.all.map{|s| {section_id: s.id}})

The idea basically is

- Build on controller the random_exam_sections to be selected
- Write a form that allows to you 'select' one option and assign the number
- Then, validate if the random_exam_section of a sections was selected (this why i made that `attr_accessor :selected`, i need a place to write if user select the exam_section)
- If was selected, save.

The trick here is build on the controller, then select on the view and validate the selected on the model. Here i made an example if you need help: https://github.com/afromankenobi/nested_attr_demo

To add sections when the random_exam_sections is already created you should probably use javascript. Maybe this railscasts can help you http://railscasts.com/episodes/196-nested-model-form-part-1

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download