chaitanya
chaitanya

Reputation: 1984

Active admin user new page got error

I have two types of admin users

  1. Super Admin
  2. Institution Admin

Using cancan for following things.

Super admin can create Institution admin / another super admin as well as normal users related to the institution, and can manage all the other things like interest types, goals....etc. Also super admin can see all the users created in the system.

Institution admin can create user only related to the institution and can see only users related to that institution.

So everything working fine unless 1 thing. When i logged in with institutional admin and go on the page to create a new user it shows me following error.

ActiveRecord::HasManyThroughNestedAssociationsAreReadonly in Admin::UsersController#new 
Cannot modify association 'AdminUser#users' because it goes through more than one other association.

models/admin_user.rb

class AdminUser < ActiveRecord::Base

  belongs_to :institution
  has_many :profiles, :through => :institution
  has_many :users, :through => :profiles

  devise :database_authenticatable, 
         :recoverable, :rememberable, :trackable, :validatable

  def password_required?
    new_record? ? false : super
  end

  def all_users
    if role == "super_admin"
     User.unscoped
    else
     #may be below line of code has issue.
     users       
    end
  end
 end

models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(current_admin_user)
    # Define abilities for the passed in user here. For example:

    current_admin_user ||= AdminUser.new # guest user (not logged in)

    case current_admin_user.role
    when "super_admin"  
      can :manage, :all  
    when "institution_admin"
      can :manage, User, :profile => {:institution_id => current_admin_user.institution_id}
      can :manage, InterestType, :institution_id => current_admin_user.institution_id 
    end
    end
   end

controllers/users_controller.rb

 class UsersController < ApplicationController
      layout "home"
      skip_before_filter :require_login, :only => [:new, :create, :activate]

      def new
        @user = User.new
        @user.build_profile
      end

      def create
        @user = User.new(params[:user])
        if @user.save
           redirect_to home_path, :notice => "Please check email."
        else
           render :new, :alert => @user.errors
        end
      end
    end

admin/admin_users.rb

ActiveAdmin.register AdminUser do

  menu :if => proc{ can?(:manage, AdminUser) }        controller.authorize_resource 

  index do
    column :email
    column ("Role") {|admin_user| admin_user.role == "super_admin" ? "Administrator" : "Institution Administrator" }  
    column ("Instituion") { |admin_user| admin_user.institution.name unless admin_user.institution_id.nil? }
    column :current_sign_in_at
    column :last_sign_in_at
    column :sign_in_count
    default_actions   end

  #...   form do |f|
    f.inputs "Admin Details" do
      f.input :email
      f.input :role, :as => :select, :include_blank => false, :collection => Hash[ "Institution Administrator", "institution_admin", "Administrator", "super_admin"]
      f.input :institution_id, :as => :select, :include_blank => false, :collection => Institution.all.map{ |ins| [ins.name, ins.id] }
    end
      f.buttons   end

  after_create { |admin| admin.send_reset_password_instructions unless admin.email.blank? and admin.institution_id.blank? }
    def password_required?
    new_record? ? false : super   end

end

admin/users.rb

ActiveAdmin.register User do

  menu :if => proc{ can?(:manage, User) }     
  controller.authorize_resource 
  scope_to :current_admin_user, :association_method => :all_users

  index do

    column :username
    column :email
    column ("Instituion") { |user| user.profile.institution.name }
    column :activation_state
    column("Name" ) {|user| user.profile.users_firstname + " " + user.profile.users_lastname}
    column :created_at
  end

  form do |f|

    f.inputs "User Details" do
      f.input :username
      f.input :email
      if f.object.id.nil?
          f.input :password
          f.input :password_confirmation
      end
    end

    f.inputs "Profile Details", :for => [:profile, f.object.profile || Profile.new] do |profile_form|
      profile_form.input :users_firstname
      profile_form.input :users_lastname
      profile_form.input :users_telephone
      profile_form.input :class_year, :collection => 1995..2020,  :selected => Time.now.year
      if current_admin_user.role == "super_admin"
        profile_form.input :institution_id, :as => :select, :include_blank => false, :collection => Institution.all.map{ |ins| [ins.name, ins.id] }
      elsif current_admin_user.role == "institution_admin"
        profile_form.input :institution_id, :as => :hidden, :value => current_admin_user.institution_id
      end
    end

    f.buttons
  end


end

Note: When i edit def all_users in admin_users.rb from users to User.scoped i can create new user from institution user but on index page i can see all the users(instead of only the users from the institution)

Upvotes: 2

Views: 1525

Answers (1)

Tanzeeb Khalili
Tanzeeb Khalili

Reputation: 7344

You can fix the error by modifying the users line in all_users to:

User.joins(:profile => :institution).where(["#{Institution.table_name}.id = ?", institution.id])

This wont set the profile on the new user, so you will still need to assign that in the form the same way you assign institution. You will also need to make sure the bi-directional associations exist between Profile and Institution.

The reason you got the error is because ActiveRecord will not allow you to create new records off of a nested has_many through association. We are are working around this by getting the results of the same association but without going through a has_many. The query is the equivalent of saying "get me all users who are part of a profile, which is in turn part of an institution, where the institution's id is the same as the current user's institution's id".

Upvotes: 1

Related Questions