I am trying to create a form to save quite a complexish relationship. The models for the relationship are below.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :parent_goals, class_name: 'GoalDependency', foreign_key: :parent_id
has_many :child_goals, class_name: 'GoalDependency', foreign_key: :child_id
has_many :children, through: :child_goals
has_many :parents, through: :parent_goals
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalDependency < ActiveRecord::Base
belongs_to :parent, class_name: 'Goal', foreign_key: 'parent_id'
belongs_to :child, class_name: 'Goal', foreign_key: 'child_id'
end
So a goal can have many parents or it can have many children or both. I have tried using a multi select drop down to save these relationships and setting child_ids/parent_ids, but that doesn't seem to work because the goal_dependency requires both fields - i.e. child_id and parent_id. Rails only sets the one. So if I am saving a list of child_ids it sets those, but it doesn't know to set the parent_id with the current goal and visa versa.
I have tried using accepts_nested_attributes, but I am not really sure how I can use this with a multi-select drop down.
Any guidance or direction on how I could approach this would be appreciated.
Example of my current form.
.row
.col-md-6
= simple_form_for(#goal) do |f|
- if #goal.errors.any?
#error_explanation
h2 = "#{pluralize(#goal.errors.count, 'error')} prohibited this goal from being saved:"
ul
-#goal.errors.full_messages.each do |message|
li = message
= f.input :description
= f.input :goal_status_id, collection: #goal_statuses, value_method: :id, label_method: :name, as: :select
= f.input :goal_type_id, collection: #goal_types, value_method: :id, label_method: :name, as: :select
= f.input :user_ids, collection: #users, as: :select, label: 'Assigned To', input_html: { class: 'chosen-select', multiple: true }, selected: #goal.user_ids
= f.input :child_ids, collection: #goals, as: :select, label: 'Children Goals', input_html: { class: 'chosen-select', multiple: true }, selected: #goal.child_ids, value_method: :id, label_method: :description
br
= f.submit
After further investigation I thought of another way to do this, but I am not really happy with it. I can split the concept of a dependency into a parent dependency and a child dependency and have these as different models. The relationship can then be handled differently. See the code below for the child dependency.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :child_goals, class_name: 'GoalChildDependency'
has_many :children, through: :child_goals
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalChildDependency < ActiveRecord::Base
belongs_to :goal
belongs_to :child, class_name: 'Goal', foreign_key: :child_id
end
From what I have read rails can't deal with composite keys and my case seems to be a case where a composite key would make sense.
Anyway if anyone can figure out how to make my initial code work that would be great.
Maybe it would be just easier to have single Goal model with self referencing parent_id column on it. Where than you can make
has_many :child_goals, class_name: 'Goal', foreign_key: :parent_id
belongs_to :parent_goal, class_name: 'Goal'
Additionally if you need ChildGoal model anyway, you can have a single table inheritance models, add column type to goals table and have ChildGoal < Goal and follow analogous approach as above.
Related
I have a self join table in rails but the view is giving infinity loop, any way to prevent this?
Node Model
has_many :child, class_name: 'Node', foreign_key: :parent_id, dependent: :destroy
belongs_to :parent, class_name: 'Node', foreign_key: "parent_id", primary_key: 'id', optional: true
### template node
= f.input :name
= f.fields_for :child
= f.link_to_add t('.add_child'), :child
### template child
= f.input :name
= f.fields_for :child # this line and below line is causing the infinity loop
= f.link_to_add t('.add_child'), :child
I'm not sure I get what you are trying to do. Why is the template for child calling f.fields_for :child ? Makes no sense for me, f should in this case be child? When you do a self join, you should ref the templates as the relationship names. So parent and child and not use node. Anyway, the problem here is most likely that you have not added accepts_nested_attributes_for.
When creating a new object and connecting it with existing (has_many :through) resources I need to:
Save the new object first
Edit this newly created object again to add connections to the nested resources.
Cumbersome! It seems ActiveAdmin tries to create the association first, then the main object. Is it somehow possible to do this object creation + associating nested resources in one go?
Is case a more concrete example is needed, here is an example of my data model and ActiveAdmin setup:
Person < ActiveRecord::Base
has_many :organizations, through: :person_organizations
end
Organization < ActiveRecord::Base
has_many :people, through: :person_organizations
end
PersonOrganization < ActiveRecord::Base
belongs_to :person
belongs_to :organization
validates :person, presence: true
validates :organization, presence: true
end
form do |f|
f.inputs do
f.input :name
end
f.inputs 'Organizations' do
f.has_many :person_organizations, do |connection_f|
connection_f.input :organization, as: :select,
collection: Organization.select[:id, :name],
member_label: proc { |org| org.name }
end
end
end
You have to add
accepts_nested_attributes_for :organizations, allow_destroy: true
and if you haven't also the
has_many :person_organizations
in your Person model, and you can place
f.input :organizations, :multiple => true
in your form. Also make sure you permit the correct params in your activadmin register block. In this case it would be
permit_params :name, :organization_ids => []
Read carefully: https://activeadmin.info/5-forms.html#nested-resources and https://activeadmin.info/2-resource-customization.html#setting-up-strong-parameters
I like to decorate multiple select inputs with the select2 javascript library. Let me know if something does not work out.
Using
<%= collection_check_boxes(:employee, :skill_ids, Skill.all, :id, :name) %>
on its own works just fine in my project. This way, each employee shows all skills and if they are checked or not.
Additionally to that, I have some categories, which the skills are related, to (Category has many Skills).
Can anyone recommend a proper way, to use the collection_check_boxes correctly for each category so only the skills related to the category are displayed?
I think I am just missing the correct limitation for the "Skill.all" parameter.
Finally it should look like this, but instead of Submit buttons (by using jquery-ujs, method: put), I want those skills to be checkboxes.
http://d.pr/i/CYHaGH
Edit1: Skills and Categories model
class Category < ApplicationRecord
has_many :subcategories, class_name: "Category", foreign_key: "parent_id"
belongs_to :topcategory, class_name: "Category"
has_many :skills
end
class Skill < ApplicationRecord
has_many :employeeskillsets, foreign_key: "skill_id"
has_many :employees, through: :employeeskillsets
has_many :projectskillsets
has_many :projects, through: :projectskillsets
has_many :tagsets, dependent: :destroy
has_many :tags, through: :tagsets
belongs_to :category
end
Edit2: Still no progress. Does anybody have an idea how this could be done?
SOLUTION:
Ok, the solution was pretty simple. I just had to declare the collection on each category, which already was for a previous version of that app. <%= collection_check_boxes :employee, :skill_ids, category.skills, :id, :name%> (category.skills was set for each instance of categories).
Thanks for the help.
Using collection_check_boxes, you can pass a block like that :
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
b.label { b.check_box }
end
And you can access value, text and more :
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
b.label(:"data-value" => b.value) { b.check_box + b.text }
end
You should read the documentation here :
https://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes
You will find all you need to solve your issue,
Hope that help
Ok, the solution was pretty simple. I just had to declare the collection on each category, which already was for a previous version of that app. <%= collection_check_boxes :employee, :skill_ids, category.skills, :id, :name%> (category.skills was set for each instance of categories).
I know that I should know this, but I cannot seem to figure it out at all and I'm still new to development...
So I have four models...
Appointments
class Appointment < ActiveRecord::Base
belongs_to :user
belongs_to :profile
belongs_to :location
end
Profiles
class Profile < ActiveRecord::Base
belongs_to :user
has_many :appointments
has_many :profile_locations
has_many :locations, through: :profile_locations
accepts_nested_attributes_for :profile_locations, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :locations, reject_if: :all_blank, allow_destroy: true
end
profile_locations
class ProfileLocation < ActiveRecord::Base
belongs_to :profile
belongs_to :location
belongs_to :location_type
accepts_nested_attributes_for :location
end
and locations
class Location < ActiveRecord::Base
has_many :profile_locations
has_many :profiles, through: :profile_locations
has_many :appointments
end
On the create appointments page, I already have an associated profile on the record. I also have an association field on my simple_form for locations that I want to be able to assign to the appointment based on those tied to the profile..
I was trying something like this, but cannot seem to getting working.
%td= f.association :location, :as => :collection_select, collection: Location.where( location.profile_location.profile_id: #profile.id ), label_method: :address_1, value_method: :id, include_blank: false, :input_html => {:class => "input-small"}, :label => "Select The Location"
Am I missing something here or is there an easier way to query this? Any guidance on any of this would be helpful.
If you are are using simple_form you should be creating collection_input like this:
%td= f.input :location, collection: Location.joins(:profile_location).where(profile_locations: { profile_id: #profile.id })
Thanks ksarunas.... I needed a minor tweak, but got it running!
%td= f.association :location, :as => :collection_select, collection: Location.includes(:profile_locations).where(profile_locations: { profile_id: #appointment.profile_id })
Was getting an error trying to pull in the #profile.id and had to pluralize the profile_locationS in both places.
i'm new to ActiveAdmin and Rails and i struggle on something to build up my ActiveAdmin interface.
Consider the following models :
class PageType < ActiveRecord::Base
has_many :fields, class_name: 'PageField'
accepts_nested_attributes_for :fields, allow_destroy: true
end
class PageField < ActiveRecord::Base
belongs_to :page_type
has_many :page_has_fields
has_many :pages, through: :page_has_fields
accepts_nested_attributes_for :page_has_fields, allow_destroy: true
end
class PageHasField < ActiveRecord::Base
belongs_to :page
belongs_to :page_field
end
class Page < ActiveRecord::Base
belongs_to :page_type
has_many :page_has_fields, dependent: :delete_all
has_many :page_fields, through: :page_has_fields
accepts_nested_attributes_for :page_fields, allow_destroy: true
end
In Active Admin I want to create some page templates to handle "static" pages. And in each of the pages, I want to update the content of each fields related to the templates page.
Thus far, what I did worked with this code :
ActiveAdmin.register Page do
permit_params :name, :page_type_id, :page_id,
:page_fields_attributes => [:id, :name, :field_type, :page_id,
:page_has_fields_attributes => [:id, :content, :page_id]
]
form do |f|
f.inputs
f.has_many :page_fields, heading: false, new_record: false do |g|
g.inputs :name, :required
g.has_many :page_has_fields, new_record: false do |h|
h.input :content if h.object.page_id == f.object.id
end
end
f.actions
end
end
But the second has_many seems really wrong to me, and i'm sure there are a better solution to this problem.
If i don't go with the "if", inputs are created for the right fields, but for every single page.
Is there a way to specify an ID or a parameter in has_many ? Or a better tag to handle situation like this ?
Thanks
Try changing your setup to something more like this
ActiveAdmin.register Page do
...
form do |f|
f.inputs do
f.input :some_column
f.input :some_other_column
f.input :page_fields, as: :check_boxes, checked: PageField.all.map(&:name)
f.input :page_has_fields, as: :check_boxes, checked: PageField.all.map(&:content)
end
f.actions
end
end