How to use ActiveAdmin on models using has_many through association? - ruby-on-rails

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
(source: rubyonrails.org)
How can I use ActiveAdmin to ...
show appointment date of each patient in physicians page?
edit appointment date of each patient in physicians page?
Thanks all. :)

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

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

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

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

#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

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.

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]

Related

Nested resource with 2-deep association

I'm trying to set up Active Admin so an AdminUser (logged in) can view a list of all ServiceUsers they are allocated, then click on a link next to a specific ServiceUser to see all support sessions they have created (for that specific ServiceUser).
Model:
class ServiceUser
has_many :support_allocations
has_many :admin_users, through: :support_allocations
has_many :support_budgets
class AdminUser < ApplicationRecord
has_many :support_allocations
has_many :service_users, through: :support_allocations
class SupportAllocation < ApplicationRecord
belongs_to :admin_user
belongs_to :service_user
has_many :support_sessions
class SupportSession < ApplicationRecord
belongs_to :invoice, optional: true
belongs_to :support_allocation
has_one :service_user, through: :support_allocation
has_one :admin_user, through: :support_allocation
I'm guessing I need to make SupportSessions a nested resource of ServiceUser so I can follow restful principles (i.e with a route to e.g. /service_users/1/support_sessions). I've managed to nest the resource like this:
ActiveAdmin.register SupportSession do
belongs_to :service_user
But I realise that omits the crucial join table SupportAllocation. Also, it doesn't work! Error message is:
NoMethodError in SupportSessionsController#index
undefined method `support_sessions' for #<ServiceUser:0x00007fe6316b9030> Did you mean? support_allocations
Previously I could view all SupportSessions for all ServiceUsers allocated to an AdminUser by using the code:
ActiveAdmin.register SupportSession do
super.includes(support_allocation: [:service_user, :admin_user])
This issue is I'd like to work with a resource in the context of the SupportAllocation relationship between a ServiceUser and an AdminUser. So the logged in AdminUser can see an index list of all ServiceUsers they are allocated (via SupportAllocation), then move to an index list of all SupportSessions for a selected ServiceUser (from that first list).
Is a nested resource the right solution here or is there a simpler way to just have a generic SupportSession resource page that can either show SupportSessions for ALL ServiceUsers or a specific ServiceUser (by 'id' in the URL)? How do I work with this tricky two-level association with an intermediary table?
Thank you so much for your help.
Here's the code for my support_sessions.rb controller in app/admin/
ActiveAdmin.register SupportSession do
menu label: 'My sessions'
#belongs_to :support_allocation
belongs_to :service_user
# Eager loading to speed up queries
includes :service_user, :admin_user, :support_allocation
sidebar :help, priority: 0 do
"Need help? Email us at help#example.com"
end
# Default sort order
config.sort_order = 'support_sessions.date_held_asc'
controller do
def scoped_collection
#super.includes :support_allocation
super.includes(support_allocation: [:service_user, :admin_user])
#SupportSession.includes(support_allocation: [:service_user, :admin_user])
#super.includes :service_user, :admin_user
end
end
scope :all, :default => true
scope :invoiced
scope :uninvoiced
index do
selectable_column
column 'Date held', :date_held
#column 'Service user', :full_name, :sortable => 'service_users.family_name' do |ss|
#service_user = ServiceUser.find(ss.support_allocation.service_user.service_user_id).full_name
#ss.support_allocation.service_user
#end
#column 'Based at', sortable: 'support_allocation.service_user.organisation.org_name' do |ss|
#ss.support_allocation.service_user.organisation.org_name
#end
column 'Venue', :venue
#column 'Support worker', :full_name, :sortable => 'admin_users.family_name' do |ss|
#support_worker = AdminUser.find(ss.admin_user_id).full_name
#ss.support_allocation.admin_user
#end
actions
end
permit_params(:admin_user_id, :service_user_id, :venue, :mode_of_delivery, :date_held, :time_start, :time_end, :total_breaks, :status)
form do |f|
f.semantic_errors *f.object.errors.keys
f.inputs 'Support session details' do
# Code to check if creating a new record or editing an existing record:
#if f.object.new_record?
#if current_admin_user.super_admin == false
# Pre-select only current support worker (so no other choices)
#f.input :admin_user, :label => 'Support worker', as: :select, collection: AdminUser.where(super_admin: 0, #id: current_admin_user.id), include_blank: false
#else
# Otherwise list all support workers
#f.input :admin_user, :label => 'Support worker', as: :select, collection: AdminUser.where(super_admin: 0), include_blank: true, allow_blank: false
#end
#f.input :support_allocation
#f.input :service_user, :label => 'Service user', as: :select, collection: ServiceUser.all, include_blank: true, allow_blank: false
f.input :mode_of_delivery, as: :select, collection: SupportSession.mode_of_deliveries.keys, include_blank: true, allow_blank: false
f.input :venue, :label => 'Venue'
f.input :date_held, :label => 'Date held', :as => :datepicker
f.input :time_start, :label => 'Start time'
f.input :time_end, :label => 'End time'
f.input :total_breaks, :label => 'Total breaks (in minutes)', :input_html => { :value => '0' }
if current_admin_user.super_admin == false
f.input :status, as: :select, collection: SupportSession.statuses.keys, include_blank: true, allow_blank: false
end
end
f.actions
end
end
I believe your ActiveAdmin registrations need to be reflected the same way as they are in your models.
For example, you have:
ActiveAdmin.register SupportSession do
belongs_to :service_user
but your SupportSession model is defined as:
class SupportSession < ApplicationRecord
...
has_one :service_user, through: :support_allocation
...
Assuming your model is correct, the ActiveAdmin registration should also be a has_one relationship.
Additionally, it looks like you have not fully defined your model relationships. Your ServiceUser model does not have a corresponding belongs_to relationship.
Thanks for the tip on my model.
To fix my original problem I nested my SupportSession ActiveAdmin controller within the SupportAllocation:
ActiveAdmin.register SupportSession do
menu label: 'My sessions'
belongs_to :support_allocation
This creates a new nested route for SupportSession within the context of SupportAllocation (which defines the relationship between a ServiceUser and a SupportSession) letting me create a link (e.g. /support_allocations/2/support_sessions) from SupportAllocation directly to all SupportSessions for an individual ServiceUser, using the usual path helper:
actions defaults: false do |support_allocation|
link_to 'Manage support sessions', support_allocation_support_sessions_path(support_allocation_id: support_allocation.id), class: 'member_link'
end

Unpermitted parameters for active_admin Nested Form

I'm using Nested form in Active admin resource but value of nested attribute not save in model.
here is detail of my models -
class Exceed::Assessment < ActiveRecord::Base
has_many :assessment_infos, :class_name => "Exceed::AssessmentInfo", :dependent => :destroy
accepts_nested_attributes_for :assessment_infos, allow_destroy: true
end
next model -
class Exceed::AssessmentInfo < ActiveRecord::Base
belongs_to :assessment
end
and here is the active admin resource of Exceed::Assessment
ActiveAdmin.register Exceed::Assessment do
form do |f|
f.inputs "Exceed Details" do
f.input :exceed_id, :label => 'Exceed Type', :as => :select, :collection => Exceed.all.map{|s| ["#{s.exceed_type}", s.id]}
f.input :exceed_section_id, :label => 'section', :as => :select, :collection => ExceedSection.all.map{|s| ["#{s.section}", s.id]}
f.input :guideline, label: "Section Guideline"
end
f.has_many :assessment_infos do |q|
q.input :information
end
f.actions
end
controller do
def permitted_params
params.permit exceed_assessment: [:exceed_id, :exceed_section_id, :guideline],
assessment_infos_attributes: [:information]
end
end
end
from my active admin form I fill details of exceed_assessment and nested form assessment_info.
Exceed_assessment details are successfully save in model but assessment_info not save.
when I check it on console it show error message -
Unpermitted parameters: assessment_infos_attributes
Unpermitted parameters: utf8, authenticity_token, commit
I did some silly mistakes, after correct them its working.
Firstly, in my Assessment_info model there is a column 'exceed_assessment_id'. I change it to 'assessment_id' because my model is belongs_to Assessment model.
Second I slightly change syntax of Permit params, like this -
controller do
def permitted_params
params.permit exceed_assessment: [:id, :exceed_id, :exceed_section_id, :guideline,
assessment_infos_attributes: [:assessment_id, :information],]
end
end

active admin edit form not pre-populating belongs_to relationship

Im using Rails 4 and I have a form within active admin that does not pre-populate or load relationship data into the edit form. If I dont define a form then the default that is loaded by active admin does, but it is shows the object and not the object.name for example so I have set out to edit it.
Here is what I have
form do |c|
c.semantic_errors *c.object.errors.keys
c.inputs "Event" do
c.input :title
c.input :date, :as => :datetime_picker, :local => true
c.input :description
end
c.inputs "Training Request" do
c.inputs :for => [:training_request, c.object.training_request || c.object.build_training_request] do |w|
list_of_training_requests = TrainingRequest.fulfilled.collect {|t| t.host.name }
w.input :id, as: :select, :collection => list_of_training_requests
end
end
c.inputs "Trainer" do
c.inputs :for => [:trainer, c.object.trainer || c.object.build_trainer] do |x|
list_of_trainers = Trainer.qualified.collect {|t| t.name }
x.input :id, as: :select, :collection => list_of_trainers
end
end
c.actions
end
The form loads without any errors and does list data just not what is set the in the database e.g.
If anyone can point me in the right direction it would be very much appreciated.
If the only problem with the original forms is that it just displays the object instead of name you could overwrite to_s in your models. For example:
class Trainer < ActiveRecord::Base
def to_s
name
end
end
Then when you call an instance of Trainer it should display the name property.

Rails Active Admin when I create my form it only displays the last field. Permit params wont accept :page field

I am having 2 problems.
1) formtastic will only show the last input field instead of all of them. In this case it will only display:
r.input :sort_order
2) I had to do some wierd wrap f.inputs around each field to get it to show up which i believe is the wrong way. But when i submit the form it says Unpermitted parameters: page. When I did clearly define page I dont know how else to get permit params accept this.
Here is my model
class Fact < ActiveRecord::Base
has_one :page, as: :pageable, dependent: :destroy
accepts_nested_attributes_for :page
end
The other model:
class Page < ActiveRecord::Base
belongs_to :pageable, polymorphic: true
end
My active admin:
ActiveAdmin.register Fact do
permit_params :id, page_attributes: [:type, :name, :description :sort_order ]
form do |f|
f.inputs "My Page", for: [:page, f.object.page || Page.new] do |r|
r.input :name
r.input :description
r.input :sort_order
end
end
end

Rails - activeadmin, duplicating has_many records upon updating "parent" record

My models are as follows:
class Project < ActiveRecord::Base
has_many :project_images
accepts_nested_attributes_for :project_images
end
class ProjectImage < ActiveRecord::Base
belongs_to :project
mount_uploader :image, ImageUploader
end
Here's the activeadmin file:
ActiveAdmin.register Project do
remove_filter :projects_sectors
permit_params :title, :info, :case_study, project_images_attributes: [:image, :cover]
index do
column :title
actions
end
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Project" do
f.input :title
f.input :info
f.input :case_study, :as => :file
end
f.inputs "Images" do
f.has_many :project_images, :allow_destroy => true, :heading => false, :new_record => true do |img_f|
img_f.input :image, :as => :file , :hint => f.template.image_tag(img_f.object.image)
img_f.input :cover
end
end
f.actions
end
end
The problem is that when i simply edit a project and click on update project, it simply duplicates all the records that exist for relationship at that point. Eg. if i have 2 images under 1 project, after changing say, the project title, i will end up with 4 images.
Hope it's clear what the issue is. Would appreciate greatly if anybody could give me a litle help.
Thanks a lot in advance.
You have to permit the id of the images: project_images_attributes: [:id, :image, :cover]
If you don't permit the id, it will be null in the action, and rails thinks it's a new record and save it.
I think this is the same problem as this one discussed on the CarrierWave wiki. Instead of generating input fields for existing images, generate an image tag and a 'remove?' option. If you generate an input field for them, then you'll end up with duplicate results.
ActiveAdmin.register Project do
controller do
def apply_filtering(chain)
super(chain).distinct
end
end
# your code
end

Resources