Puneet Pandey
Puneet Pandey

Reputation: 555

ActiveAdmin has_many through edit 1k records

I have has_many through relationship setup between my model Company and Group. Here's the model code:

class Company < ActiveRecord::Base
  has_many :company_groups
  has_many :groups, through: :company_groups

  accepts_nested_attributes_for :company_groups, allow_destroy: true
end

class Group < ActiveRecord::Base
  has_many :company_groups
  has_many :companies, through: :company_groups

  accepts_nested_attributes_for :company_groups, allow_destroy: true
end

class CompanyGroup < ActiveRecord::Base
  belongs_to :company
  belongs_to :group

  validates_uniqueness_of :company_id, :scope => :group_id
end

The problem I have is, in my company_groups table there are 900 records for group_id=1

Now, if I am giving option to edit the group, it tries to load all 900 records at one go.

Here's my /app/admin/group.rb file:

form do |f|
  return unless current_admin_user.super_admin?
  f.semantic_errors *f.object.errors.keys
  f.inputs 'Group Information' do
    f.input :name
  end
  f.has_many :company_groups do | company_group |
    if !company_group.object.nil?
      company_group.input :_destroy, :as => :boolean, :label => "Destroy?"
    end
    if company_group.object.new_record?
      company_group.input :company, :label => 'Company', :as => :select, :collection => @companies
    else
      company_group.input :company, :input_html => { :readonly => "readonly", :value => company_group.object.company.name }, :as => :string
    end
  end
  f.actions
end

The page was taking hell lot of time to load, so I have enabled the check, if company_group is not a new object, show the company.name in textbox (in readonly form). Though, this bring the page load time to < 25 seconds but I still think this is not a good practice to load these many records at once (as this could hang the browser as well)

I will bifurcate my question into 2 parts:

  1. From optimisation perspective what would be the best way to edit those records (Search/Lazy loading etc)? And if anyone has done this thing earlier or similar kind of functionality, can you share code snippet to achieve

  2. From ActiveAdmin perspective, does ActiveAdmin provides a convenient way to edit records in batches?

Any help would be highly appreciated!

Best, Puneet

EDIT: 05/Apr/17

Here's what I am doing to edit 1k records:

  1. Allowing add|edit groups on Company new/edit pages.
  2. Allowing add company to group via button on Group show/edit pages.
  3. Allowing edit|delete company from group via Group show page.
  4. CODE: My model code for company will remain as-is
  5. CODE: My model code for group will remain as-is

Here is my full code snippet:

class CompanyGroup < ActiveRecord::Base
  validates_uniqueness_of :company_id, :scope => :group_id

  belongs_to :company
  belongs_to :group

  before_save :ensure_settings

protected
  def ensure_settings
    errors.add(:field_name_2, "You are not allowed to change this field when #{company.boolean_field_name} is set to TRUE") and return false if company.boolean_field_name && field_name_2_changed?
  end
end

Here's my file /app/admin/company.rb

ActiveAdmin.register Company do
  form do |f|
    f.semantic_errors
    f.inputs do
      f.input :field_1
      f.input :boolean_field_name
      f.has_many :company_groups do | company_group |
        company_group.input :group
        company_group.input :field_name_2, as: :radio, collection: [ ['Yes', true], ['No', false] ]
      end
    end
  end

  show do
    row :field_1
    row :boolean_field_name
    panel "Groups" do
      table_for company.company_groups do
        column "Group Name" do | company_group |
          company_group.group.name
        end
        column "Field Name 2" do |company_group|
          company_group.group.field_name_2
        end
      end
    end
  end
end

Here's my file /app/admin/group.rb

ActiveAdmin.register Group do
  config.action_items[0] = ActiveAdmin::ActionItem.new :show, only: [:show, :edit] do
    link_to 'Add Company to Group', new_company_group_path(group_id: group.id)
  end

  form do |f|
    f.semantic_errors *f.object.errors.keys
    f.inputs do
      f.input :name
      unless f.object.company_groups.any?
        f.has_many :company_groups do | company_group |
          company_group.input :company, as: :select
          company_group.field_name_2, as: :radio, collection: [ ['Yes', true], ['No', false] ]
        end
      end
    end
    f.actions
  end

  show do
    attributes_table do
      row :name
    end

    panel "Companies" do
      table_for group.company_groups do
        column "" do |company_group|
          link_to("Edit", edit_company_group_path(company_group, group_id: group.id)) + "&nbsp;|&nbsp;".html_safe + (link_to "Delete", company_group_path(company_group), method: :delete, data: { confirm: "Are you sure you want to delete this company from this group?" })
        end
      end
    end
  end
end

And finally my /app/models/company_group.rb file:

ActiveAdmin.register CompanyGroup do
  form do |f|
    f.semantic_errors *f.object.errors.keys
    f.inputs do
      f.input :group
      f.input :company
      f.input :field_name_2, as: :radio, collection: [ ['Yes', true], ['No', false] ]
    end
  end

  controller do
    def create
      create! do |format|
        format.html { redirect_to(new_company_group_path(group_id: @company_group.group_id)) }
      end
    end

    def update
      update! do |format|
        format.html { redirect_to(group_path(@company_group.group)) }
      end
    end
  end
end

But now I am running to different issues:

  1. My custom model validation for company_group.rb is not working. That means, whenever I am trying to create/update company record and boolean_field_name is set to TRUE and field_name_2 gets changed, I am not getting (a) error message, (b) though updated value of field_name_2 is not updating in DB but I am not even stopped on create/update action. It takes me back to the show page.
  2. Same is the case when I am trying to update a edit company_group object (via Group show/edit page). I expect to stay on POST/PATCH page of company_group with error message but I am getting redirected. Is that because of customise actions for create and update?

EDIT: 05/Apr/17

I've even tried checking valid? for company_group record by:

def create
  create! do |format|
    if resource.valid?
      format.html { redirect_to(new_company_group_path(group_id: @company_group.group_id)) }
    end
  end
end

def update
  if resource.valid?
    update! do |format|
      format.html { redirect_to(group_path(@company_group.group)) }
    end
  end
end

But no luck!

Here're the logs:

Started PATCH "/company_groups/877" for ::1 at 2017-04-05 16:51:41 +0530
Processing by CompanyGroupsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"T7jMdOHQygqiX5RSsEkfD1l0h5Q+dpaNrmJlhtiPhpaxen6AyBcv4yD7xAy2GefYpGX0Bvgx4oD7vTMsfIl6fw==", "company_group"=>{"group_id"=>"6", "company_id"=>"623", "field_name_2"=>"true"}, "commit"=>"Update Company group", "id"=>"877"}
[1m[36mCompanyGroup Load (0.3ms)[0m  [1mSELECT  "company_groups".* FROM "company_groups" WHERE "company_groups"."id" = $1 LIMIT 1[0m  [["id", 877]]
[1m[35m (0.3ms)[0m  BEGIN
[1m[36mCompanyGroup Exists (0.5ms)[0m  [1mSELECT  1 AS one FROM "company_groups" WHERE ("company_groups"."company_id" = 623 AND "company_groups"."id" != 877 AND "company_groups"."group_id" = 6) LIMIT 1[0m
[1m[35mCompany Load (0.3ms)[0m  SELECT  "companies".* FROM "companies" WHERE "companies"."id" = $1  ORDER BY companies.name LIMIT 1  [["id", 623]]
[1m[36m (0.3ms)[0m  [1mROLLBACK[0m
[1m[35mCompanyGroup Exists (0.5ms)[0m  SELECT  1 AS one FROM "company_groups" WHERE ("company_groups"."company_id" = 623 AND "company_groups"."id" != 877 AND "company_groups"."group_id" = 6) LIMIT 1
[1m[36mGroup Load (0.3ms)[0m  [1mSELECT  "groups".* FROM "groups" WHERE "groups"."id" = $1 LIMIT 1[0m  [["id", 6]]
Redirected to http://localhost:3000/superadmin/groups/6
Completed 302 Found in 94ms (ActiveRecord: 7.8ms)

Upvotes: 1

Views: 1000

Answers (1)

Puneet Pandey
Puneet Pandey

Reputation: 555

Finally, I get it resolved by changing the before_save callback to validate. The updated post from 5th/Apr will continue to remain same the only change I did is in /app/models/company_group.rb file.

class CompanyGroup < ActiveRecord::Base
  validates_uniqueness_of :company_id, :scope => :group_id

  belongs_to :company
  belongs_to :group

  validate :ensure_settings

protected
  def ensure_settings
    errors.add(:field_name_2, "You are not allowed to change this field when #{company.boolean_field_name} is set to TRUE") and return false if company.boolean_field_name && field_name_2_changed?
  end
end

That's it. Thanks to one of my friend for suggesting me this. :)

Upvotes: 0

Related Questions