56

I am using ActiveAdmin gem in my project.

I have 2 models using has_many through association. The database schema looks exactly the same as the example in RailsGuide. http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association has_many through association
(source: rubyonrails.org)

How can I use ActiveAdmin to ...

  1. show appointment date of each patient in physicians page?
  2. edit appointment date of each patient in physicians page?

Thanks all. :)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Victor Lam
  • 3,646
  • 8
  • 31
  • 43

7 Answers7

84

For 1)

show do
  panel "Patients" do
    table_for physician.appointments do
      column "name" do |appointment|
        appointment.patient.name
      end
      column :appointment_date
    end
  end
end

For 2)

form do |f|
  f.inputs "Details" do # physician's fields
    f.input :name
  end

  f.has_many :appointments do |app_f|
    app_f.inputs "Appointments" do
      if !app_f.object.nil?
        # show the destroy checkbox only if it is an existing appointment
        # else, there's already dynamic JS to add / remove new appointments
        app_f.input :_destroy, :as => :boolean, :label => "Destroy?"
      end

      app_f.input :patient # it should automatically generate a drop-down select to choose from your existing patients
      app_f.input :appointment_date
    end
  end
end
armandomiani
  • 732
  • 6
  • 16
PeterWong
  • 15,951
  • 9
  • 59
  • 68
  • 2
    I'm doing a similar thing, but it ends up with a SQL nightmare. Each call to appointment.patient generates another SQL query.. standard n+1 query problem. Has anyone found a way of doing the equivalent of ActiveRecord's eager loading? Eg .includes(:patient) – tomblomfield Dec 14 '11 at 18:29
  • I have some trouble with your solution. The _destroy input won't work in my case – Awea Feb 06 '12 at 11:23
  • there could be many reasons and "won't work" isn't enough for me to know what exactly goes wrong. I am sorry about that. – PeterWong Feb 06 '12 at 11:33
  • 19
    *Quick Tip*: Make sure to include accepts_nested_attributes_for in the Patients model `accepts_nested_attributes_for :appointments` – Greg May 23 '12 at 16:31
  • 4
    To anyone having similar issues to @Awea and being unable to destroy added items, ensure that `:allow_destroy` is enabled for nested attributes, see [related answer](http://stackoverflow.com/questions/4699765/rails-3-paperclip-formtastic-deleting-image-attachments) – Robin Pyon Dec 26 '12 at 04:26
  • in case anyone is interested in the index page: http://www.eileenbuildswithrails.com/posts/has-many-relationships-in-activeadmin – chech Jun 10 '13 at 16:35
  • Can we use the same with HABTM associations? – Mohd Anas Nov 18 '13 at 06:27
  • Will the approach allow update each appointment's data separately without a whole page reload? – Малъ Скрылевъ May 23 '14 at 13:09
  • @МалъСкрылевъ no, that code generates form fields for appointments, just a part of the whole form. So have to submit altogether. – PeterWong May 24 '14 at 02:47
  • Correction for 2) - it should be : `if !app_f.object.id.nil?` – Oz Ben-David Mar 19 '21 at 18:27
  • In the case of using strong parameters, you should define the above in the active admin file. – Oz Ben-David Mar 19 '21 at 19:19
15

In answer tomblomfield follow up question in comments:

Try the following in your AA ActiveAdmin.register Model do block:

  controller do
    def scoped_collection
      YourModel.includes(:add_your_includes_here)
    end
  end

This should lazy load all your associations for each index page in a separate query

HTH

Nazar
  • 1,499
  • 12
  • 24
  • 1
    `scoped_collection` is not called on the `show` view, so this won't solve tomblomfield's issue, since he was talking about N+1 queries on the `show` view. I'm running into this too, and haven't found a solution. – monfresh Jul 31 '13 at 06:39
  • I think what you are looking for is `def find_resource`, where you could run `Foo.includes(:bars).find(params[:id])` – Tashows Jan 22 '18 at 14:10
1

It should solve the N+1 query problem.

show do
  panel "Patients" do
    patients = physician.patients.includes(:appointments)
    table_for patients do
      column :name
      column :appointment_date { |patient|    patient.appointments.first.appointment_date }
    end
  end
end
Manikandan
  • 3,025
  • 2
  • 19
  • 28
1

It's work for me (with chosen)

permit_params category_ids: []

form do |f|
   inputs 'Shop' do
     input :category_ids, collection: Category.all.collect {|x| [x.name, x.id]}, as: :select, multiple: true, input_html: { class: "chosen-input",  style: "width: 700px;"}
    end
   f.actions
end
Vlad Hilko
  • 1,104
  • 12
  • 17
0

@monfresh @tomblomfield you can do

has_many :appointments, ->{ includes(:patients) }, :through => :patients

in the physicians model

...or, I'm not sure if you can use it with formtastic but you could make the scope optional with something like

has_many :appointments :through => :patients do
  def with_patients
    includes(:patients)
  end
end

and appointment.patient wont n+1 anymore

Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
thrice801
  • 1,671
  • 5
  • 23
  • 31
0

If you would like show multiple field in a panel row you can use following view:

show do |phy|
   panel "Details" do
        attributes_table do
          ... # Other fields come here
          row :appointment_dates do
            apps=""
            phy.appointments.all.each do |app|
              apps += app.patient.name + ":" + app.appoinment_date + ", "
            end
            apps.chomp(", ")
          end
        end      
   end
end

To place it in you redit form first put appointment_ids to permitted list:

permit_params: appointment_ids:[]

Add has many relationship to the form

form do |f|
   f.has_many :appointments do |app|
     app.inputs "Appointments" do
       app.input :patients, :as => :select, :label => "Assigned Patients"
       app.input :appointment_date
     end  
   end
end

Should work if there is no coding error.

Tahsin Turkoz
  • 4,356
  • 1
  • 27
  • 18
0

Regarding #2, it should be like this:

form do |f|
  f.inputs 'Physician Details' do
    f.input :name
  end

  f.inputs 'Physician Appointments' do
    f.has_many :appointments,
               heading: false,
               new_record: 'Add new appointment',
               remove_record: 'Delete appointment',
               allow_destroy: true do |app|
    app.input :patient, label: 'Choose the patient', collection: Patient.pluck(:name, :id)
    app.input :appointment_date
  end
end

Regarding the heading: - it can be false or some label (string)

Regarding the allow_destroy: - you can set it check for the user Administrator privilege's as can seen here

Important - In the Physician model, make sure to have

accepts_nested_attributes_for :appointments, allow_destroy: true

And, in the active admin model file - admin\physicians.rb - set this:

permit_params :name, appointments_attributes: [:patient_id, :_destroy, :id]
Oz Ben-David
  • 1,589
  • 1
  • 16
  • 26