1

Referring to this post: Rails: Deal with several similar model classes?

Using combination of STI and Store feature to organize very similar models.

"Declare a base model for Student with a text column called settings.

class Student < ActiveRecord::Base
  store :settings
  # name, email, phone, address etc..
end

class HighSchoolStudent < Student
  # declare HighSchoolStudent specific attributes 
  store_accessor :settings, :gpa
end

How can I create a form for a HighSchoolStudent, while remaining under the Student controller?

I dont want to add a separate controller or route resource for HighSchoolStudent, is there a way to have one form for Student and HighSchoolStudent, with a checkbox to indicate whether its a Student or a HighSchoolStudent? Can I only require that the extra attributes created for the subclass are only required for the form to be submitted if that specific class is checked?

<%= simple_form_for(@student, html: {class: "form-horizontal"}) do |f| %>
<%= f.input :name, as: :text, input_html: {rows: "1"} %>
<%= f.input :email, as: :text, input_html: {rows: "2"} %>

<%= f.input :gpa, as: :text, input_html: {rows: "1"} %>


<%= f.button :submit, class: "btn btn-primary" %>
Community
  • 1
  • 1
Katie H
  • 2,283
  • 5
  • 30
  • 51

2 Answers2

2

Sure, you can make an arbitrary checkbox, and check its value in the create action:

# In your view
<%= check_box_tag :high_school %>

# In your controller
def create
  base_class = params[:high_school] ? HighSchoolStudent : Student
  base_class.create(params[:student])
  ...
end

Any validations specific to HighSchoolStudent will be ensured only if the checkbox was checked.

PinnyM
  • 35,165
  • 3
  • 73
  • 81
  • I'm getting the error "undefined method `gpa'" How do I get the form to know that this is for both Student and HighSchoolStudent, it looks like it's not recognizing the attributes of HighSchoolStudent. – Katie H Sep 03 '13 at 18:29
  • Can you look at the form above in the question, I just added it, I'm getting the error "undefined method `gpa'" How do I get the form to know that this is for both Student and HighSchoolStudent, it looks like it's not recognizing the attributes of HighSchoolStudent – Katie H Sep 03 '13 at 18:33
  • A simple fix would be to load `@student = HighSchoolStudent.new` in your `new` action since HighSchoolStudent is guaranteed to have all attributes. If that won't work for you, you'll need to create them as generic 'unaffiliated' fields, rather than using `f.input`, and then merge them back in your create action. – PinnyM Sep 03 '13 at 18:38
  • Thanks! I'm getting an error for HighSchoolStudent path, what do I put in the routes file? I want HighSchoolStudents to be under the path for Students – Katie H Sep 03 '13 at 18:49
  • @KatieHeidmann: Add the path to your form: `<%= simple_form_for(@student, url: new_student_path, html: {class: "form-horizontal"}) do |f| %>` – PinnyM Sep 03 '13 at 18:55
2

Rails STI uses the type special column to store the class name. Student.new(type: 'HighSchoolStudent') would build a Student which would later act as a HighSchoolStudent.

This solution would not run validations, callbacks, etc. defined only for HighSchoolStudent but would run everything from Student.

To do that, you could do something like @PinnyM suggested, or you could have a hidden input for the type attribute: hidden_field_tag :type, 'Student' followed by a check_box_tag :type, 'HighSchoolStudent' and use that to determine the right class to create, which does buy you all of the validations and callbacks while being more maintainable as it uses the correct attribute.


All that to say, STI may be the wrong solution for you here.

Can I only require that the extra attributes created for the subclass are only required for the form to be submitted if that specific class is checked?

This tells me that you've added - to your students table - attributes which are irrelevant to non-HighSchool students.

A better solution might be to have a class like HighSchoolStudentProfile as an association and a method high_schooler? on student that checks for its presence. This prevents you from running into a problem I've faced before, which is having a "sparse table" and which generally grows into needing various conditional validations and has complex logic.

Community
  • 1
  • 1
Caleb Hearth
  • 3,315
  • 5
  • 30
  • 44