Search code examples
ruby-on-railsdatabasedeviseactiveadminmodels

Ruby on Rails, User Models and Active Admin query


I am using Active Admin in my application.

The application has a Users model (Devise) but I have 3 user groups - parents, teachers and students. Each has their own model.

e.g

student.rb

belongs_to :user

user.rb

has_one :student



parent.rb

belongs_to :user

user.rb

has_one :parent

What I am trying to do here is store the attributes that would be the same across the user groups in the Users model so "first_name, last_name, email" etc and then using the individual user models to store attributes that are unique to each user group. I am using a one to one relationship between the main users table and the three user group tables.

Here (note I have not displayed all attributes in the user group tables):

enter image description here

The problem is this is not working out so well with Active Admin. In the view I have to create a user and then assign a parent and a student in different views. I would like to have a single form where, for example, a student's user record can be put in with the attributes from the main Users table as well as their unique attributes in the students table without having to go to multiple model views to complete records. It's too convoluted.

Is there a way to render the inputs from different models in the one view? i.e in students have "first name", "last name"...from the Users table along with the inputs unique to a student?

If you were taking the approach I am taking here what method would you follow? I'm not even sure I am doing the associations right. In fact i'm not even sure this is the most efficient way to do this.

I am using enums to assign the roles.

Would appreciate any guidance here. Thanks.


Solution

  • Students, Guardians, and Teachers are all a type of User. Why not consolidate them into a single type and use inheritance to represent them as a common type?

    class User < ActiveRecord::Base 
      # ...
    end
    
    class Teacher < User
      # ...
    end
    
    class Guardian < User
      # ...
    end
    
    class Student < User
      # ...
    end
    

    You'll need to add a type column via a migration, and also write a task to merge them into a common table, but it will likely be worth the effort.

    See STI in Rails: http://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html.

    EDIT:

    If you don't want to go the STI route, you'll have to use nested attributes

    class User < ActiveRecord::Base 
      accepts_nested_attributes_for :teacher
      accepts_nested_attributes_for :guardian
      accepts_nested_attributes_for :student
    end
    

    Then add a custom form in admin/user.rb ():

    ActiveAdmin.register User do
      form do |f|
        f.inputs "User Inputs" do
          # ...
          f.input :email
          # ...
        end
    
        f.inputs "Guardian Attributes", for: [:guardian, f.object.guardian || Guardian.new] do |g|
          g.input :mobile_no
        end
    
        # Repeat for teacher, Student, etc...
      end
    end
    

    If you're using strong parameters, make sure you whitelist the appropriate params for User, guardian_attributes, etc.

    In my opinion, the nesting here is pretty ugly and having this normalized schema is going to be difficult to maintain and query.