Editing many-to-many relations in Activeadmin - ruby-on-rails

I am looking for a way to edit/add keywords related to an article, inline in Activeadmin.
I have defined a simple many-to-many setup:
class Area < ActiveRecord::Base
has_many :area_keywords
has_many :keywords, :through => :area_keywords
accepts_nested_attributes_for :keywords, :reject_if => :all_blank, :allow_destroy => true
end
class AreaKeyword < ActiveRecord::Base
belongs_to :area
belongs_to :keyword
end
class Keyword < ActiveRecord::Base
has_many :area_keywords
has_many :areas, :through => :area_keywords
end
I would like to add and edit the keywords in en Area form, so I setup this in Aciveadmin:
ActiveAdmin.register Area do
form do |f|
f.inputs "Area details" do
f.input :title
f.input :description
end
f.has_many :keywords do |k|
if k.object.nil?
k.input :word, :label => 'Keyword'
else
k.input :word, :label => k.object.word
k.input :_destroy, :as => :boolean, :label => "delete"
end
end
end
end
This works as expected.
But if I add the same Keyword to two different areas, the Keyword will just be created twice.
When entering a new keyword (in the Area form), I would like it to automatically create a relation to an existing keyword, or create a new keyword, if it does not exist. What would be the best way to go about it?

This is a pretty late answer :) but I actually have encountered kind of a similar issue in one of my projects...I had to add keywords/tags to two different models, but they could share them. At first I did just like you, and it was creating a record for each time you "attach" a keyword/tag to a model.
A better way to handle it is to use a tagging system. And I achieved a pretty neat system by combining two really good gems: 'acts-as-taggable-on' (https://github.com/mbleigh/acts-as-taggable-on) and 'select2-rails' (https://github.com/argerim/select2-rails)
In my own project, I actually did something similar to you and created a model just to have a list of all the appropriate keywords I wanted. But 'act-as-taggable-on' doesn't necesarilly requires a list of accepted keywords...so you can create them on the fly, and it will automatically handle duplicates, counts etc.
'select2-rails' just allows you to have an nice interface to add and remove keywords in one field, rather than using checkboxes, select options, or a text input where you would have to manually separate your string with commas or any separators.
If anyone need more details on how I implemented all, I would be more than glad to provide more code .. but the documentation for both of them are pretty straightforward!
EDIT: Well, I have a feeling some code would actually be useful :)
Bundle install both gem in your Gemfile
gem 'acts-as-taggable-on'
gem 'select2-rails'
In your Area model, you could add the following and do something like
class Area < ActiveRecord::Base
# .. your code
attr_accessible :area_keyword_list
acts_as_taggable_on :area_keywords
end
And in your ActiveAdmin file
ActiveAdmin.register Area do
form do |f|
f.inputs do
# .. whatever fields you have
f.input :area_keyword_list,
:as => :select,
:multiple => :true,
:collection => # here either a list of accepted keyword..or just left open,
:input_html => { :class => "multiple-select" }
end
end
end
and the JS for select2 is quite simple ...
$(".multiple-select").select2();
VoilĂ  !

Related

Creating a form that breaks up a has_many association?

As referenced in my earlier post rails has_many manager, I am trying to create a polymorphic imaging system which will allow any item to inherit the ability to have a cover photo and additional photos.
To accomplish that kind of imaging system, I sided with a polymorphic model with belongs_to :imageable and extended its active record capabilities out to a module named Imageable.
My main question is, given that for example we have a class called Object, how can I create a form that only targets the first Object's has_many association (the cover), and then separately administer the other has_many associations?
The form would look like..
--- Form for cover photo ----
Upload button for object[image_attributes][0][public_id]
--- Form for additional photos ---
Upload button for object[image_attributes[1][public_id]
image.rb
class Image < ActiveRecord::Base
attr_accessible :public_id
# Setup the interface that models will use
belongs_to :imageable, :polymorphic => true
end
Imageable.rb
module Imageable
extend ActiveSupport::Concern
included do
has_many :images, :as => :imageable, :dependent => :destroy # remove this from your model file
accepts_nested_attributes_for :images
validates :images, :presence => { :message => "At least one image is required" }
end
def cover
cover = images.where(:cover => true).first
if not cover
return Image.new
end
return cover
end
def additional_images
images.where(:cover => false).all
end
end
Form
<%= form.semantic_fields_for :images do |image_fields| %>
<%= image_fields.cl_image_upload(:public_id, :crop => :limit, :width => 1000, :height => 1000,
:html => {:class => "cloudinary-fileupload"}) %>
...
The above produces appropriate routes like object[image_attributes][0][public_id]
Thanks!
I would recommend that you model your relationships slightly differently by using an explicit has_one relationship from the 'Object' to the cover Imageable and a separate has_many relationship for the additional images.
If you want all the images in the same collection then have a look at this post:
How to apply a scope to an association when using fields_for?
It explains how you can specify a 'scope' or subset of the entries in the has_many collection when setting up the fields_for helper. It should work with the semantic_fields_for helper as well since it simply wraps the Rails fields_for helper.

Rails 3.2.8 & JQuery Tokeninput: Trying to add new records for has_many through relationship instead of replacing all current records in a form

I have a database of skills that relate to each other as prerequisites to each other. In an index of skills, I'd like to be able to search through other skills and add 1 or more as prerequisites. It's important to note that I ONLY want the user to be able to add prerequisites, not remove them, as that's taken care of through an up-down voting system. I'm using JQuery Tokeninput and actually have all of this working except for one thing: I can't figure out how to only add prerequisites, rather than replacing all the prerequisites for a particular skill on submit.
Models:
class Skill < ActiveRecord::Base
attr_accessible :skill_relationship_attributes, :prereq_tokens
has_many :skill_relationships
has_many :prereqs, :through => :skill_relationships
has_many :inverse_skill_relationships, :class_name => 'SkillRelationship', :foreign_key => "prereq_id"
has_many :inverse_prereqs, :through => :inverse_skill_relationships, :source => :skill
attr_reader :prereq_tokens
accepts_nested_attributes_for :skill_relationships, :allow_destroy => true
def prereq_tokens=(ids)
self.prereq_ids = ids.split(",")
end
end
class SkillRelationship < ActiveRecord::Base
attr_accessible :skill_id, :prereq_id, :skill_attributes, :prereq_attributes
belongs_to :skill
belongs_to :prereq, :class_name => 'Skill'
end
JQuery:
$('#skill_prereq_tokens').tokenInput('/skills.json',
{ theme:'facebook',
propertyToSearch:'title',
queryParam:'search',
preventDuplicates:'true'
});
View:
<%= simple_form_for skill do |f| %>
<%= f.input :prereq_tokens %>
<%= f.submit %>
<% end %>
I feel a bit silly for not getting this before, but I solved my problem by changing how prereq_tokens became prereq_ids in my Skill model.
I just changed this:
def prereq_tokens=(ids)
self.prereq_ids = ids.split(",")
end
to this:
def prereq_tokens=(ids)
self.prereq_ids += ids.split(",")
end
That's it. That little plus sign before the equals sign. I hope this helps anyone else who codes too long without a break!

Rails forms for has_many through association with additional attributes?

How can I generate form fields for a has_many :through association that has additional attributes?
The has_many :through relationship has an additional column called weight.
Here's the migration file for the join table:
create_table :users_widgets do |t|
t.integer :user_id
t.integer :widget_id
t.integer :weight
t.timestamps
end
The models look like this:
User
has_many :widgets, :through => :users_widgets,
:class_name => 'Widget',
:source => :widget
has_many :users_widgets
accepts_nested_attributes_for :widgets # not sure if this is necessary
Widget
has_many :users, :through => :users_widgets,
:class_name => 'User',
:source => :user
has_many :users_widgets
accepts_nested_attributes_for :users # not sure if this is necessary
UsersWidget
belongs_to :user
belongs_to :widget
For the sake of simplicity, Widget and User only have one field of their own called name, ergo User.first.name and Widget.first.name
Questions:
How would I append a dropdown selection for Widgets with the corresponding weight to the User create/edit form?
How can I dynamically add additional Widget forms to Users or User forms to Widgets to easily add or remove these relationships? The nested_form_for gem seems to be the perfect solution but I haven't been able to get it working.
Apart from the models and the form partials, are there any changes that need to be made to my controller?
Quick note.. I'm not interested in creating new Widgets in the User form or new Users in the Widget form, I only want the ability to select from existing objects.
I'm running Rails 3.1 and simple_form 2.0.0dev for generating my forms.
I will be solving your problem using cocoon, a gem I created to handle dynamically nested forms. I also have an example project to show examples of different types of relationships.
Yours is not literally included, but is not that hard to derive from it. In your model you should write:
class User
has_many :users_widgets
has_many :widgets, :through -> :user_widgets
accepts_nested_attributes_for :user_widgets, :reject_if => :all_blank, :allow_destroy => true
#...
end
Then you need to create a partial view which will list your linked UserWidgets. Place this partial in a file called users/_user_widget_fields.html.haml:
.nested-fields
= f.association :widget, :collection => Widget.all, :prompt => 'Choose an existing widget'
= f.input :weight, :hint => 'The weight will determine the order of the widgets'
= link_to_remove_association "remove tag", f
In your users/edit.html.haml you can then write:
= simple_form_for #user do |f|
= f.input :name
= f.simple_fields_for :user_widgets do |user_widget|
= render 'user_widget_fields', :f => user_widget
.links
= link_to_add_association 'add widget', f, :user_widgets
Hope this helps.
base your question. I made a simple App.
source is here: https://github.com/yakjuly/nest_form_example
I deployed it to heroku, you can open page: http://glowing-lightning-1954.heroku.com/users/new
answers
you want user form can select widget with weight, need do more work.dropdown selection can not satisfy your requirement.
I mix "nested_form" in a "bootstrap-rails" plugin, you can add the nested_fields easier
in my example, you need add a action calls select, and make WidgetsController#create can responsed_with :js
the code is based simple_form, you can have a try.
Thanks a lot for the cocoon pointer nathanvda. I have been scratching my head about some problems I had when trying to implement this under rails 4.0.0-rc1 and I thought I would share my findings just in case someone has the same problems when attempting this udner rails4.
Using the above code as an example, I did add user_id and widget_id to the permitted parameters as they are saved in the connecting table user_widgets. In rails 3 you did have to add them to attr_accesible in the user model but in rails 4 you have to add them to the allowed parameters in the controller of the main model you use for nesting, so here that would be the users_controller:
params.require(:user).permit(...user_fields...,
user_widgets_attributes: [:user_id, :widget_id])
Doing only this you end up with several of problems:
Every association (widget) gets multiplied when updating a user record. 1 becomes 2, 4, 8, and so on, when updating and saving the record.
removing an association does not work, the field is removed from the form but the association remains in the DB.
To fix these problems you also need to add :id and :_destroy to the list of permitted attributes:
params.require(:user).permit(...user_fields...,
user_widgets_attributes: [:user_id, :widget_id, :id, :_destroy])
after that it works flawlessly.
Juergen
PS: For now you have to use the git repository in your Gemfile to use cocoon under rails 4 until a rails 4 compatible gem is released. Thanks for the email nathanvda on my bug report!!

ActiveAdmin forms with has_many - belongs_to relationships?

I have the models Home and Photo, which have a has_many - belongs_to relationship (a polymorphic relationship, but I dont think that matters in this case). I am now setting up active admin and I would like admins to be able to add photos to homes from the homes form.
The photos are managed by the CarrierWave gem, which I dont know if will make the problem easier or harder.
How can I include form fields for a different model in the Active Admin Home form? Any experience doing something like this?
class Home < ActiveRecord::Base
validates :name, :presence => true,
:length => { :maximum => 100 }
validates :description, :presence => true
has_many :photos, :as => :photographable
end
class Photo < ActiveRecord::Base
belongs_to :photographable, :polymorphic => true
mount_uploader :image, ImageUploader
end
Try something like this in app/admin/home.rb:
form do |f|
f.inputs "Details" do
f.name
end
f.has_many :photos do |photo|
photo.inputs "Photos" do
photo.input :field_name
#repeat as necessary for all fields
end
end
end
Make sure to have this in your home model:
accepts_nested_attributes_for :photos
I modified this from another stack overflow question: How to use ActiveAdmin on models using has_many through association?
You could try this:
form do |f|
f.semantic_errors # shows errors on :base
f.inputs # builds an input field for every attribute
f.inputs 'Photos' do
f.has_many :photos, new_record: false do |p|
p.input :field_name
# or maybe even
p.input :id, label: 'Photo Name', as: :select, collection: Photo.all
end
end
f.actions # adds the 'Submit' and 'Cancel' buttons
end
Also, you can look at https://github.com/activeadmin/activeadmin/blob/master/docs/5-forms.md (See Nested Resources)
I guess you are looking for a form for a nested model. Take a look at following railscasts.
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
I cannot tell you much about active_admin, but I think this should not make a difference in handling the nested model.
I have a has_one model, like this:
f.has_many :addresses do |a|
a.inputs "Address" do
a.input :street ... etc.
While this correctly reflects our associations for Address (which is a polymorphic model) using f.has_one fails. So I changed over to has_many and all's well. Except now we have to prevent our users from creating multiple addresses for the same entity.

Using fields from an association (has_many) model with formtastic in rails

I searched and tried a lot, but I can't accomplish it as I want.. so here's my problem.
class Moving < ActiveRecord::Base
has_many :movingresources, :dependent => :destroy
has_many :resources, :through => :movingresources
end
class Movingresource < ActiveRecord::Base
belongs_to :moving
belongs_to :resource
end
class Resource < ActiveRecord::Base
has_many :movingresources
has_many :movings, :through => :movingresources
end
Movingresources contains additional fields, like quantity. We're working on the views for 'bill'. Thanks to formtastic to simplify the whole relationship thing by just writing
<%= form.input :workers, :as => :check_boxes %>
and i get a real nice checkbox list. But what I haven't found out so far is: How can i use the additional fields from 'movingresource', next or under each checkbox my desired fields from that model?
I saw different approaches, mainly with manually looping through an array of objects and creating the appropriate forms, using :for in a form.inputs part, or not. But none of those solutions were clean (e.g. worked for the edit view but not for new because the required objects were not built or generated and generating them caused a mess).
I want to know your solutions for this!
Okay, I missed the revolution of accepts_nested_attributes_for, this explains why it's not really working.
This got me a big step further, but I think somewhere I will still have some complications with my complex relations ^_^
class Moving < ActiveRecord::Base
has_many :movingworkers, :dependent => :destroy
has_many :workers, :through => :movingworkers
accepts_nested_attributes_for :movingworkers
end
<% form.inputs :for => :movingworkers do |movingworker| %>
<%= movingworker.inputs :worker, :quantity %>
<% end %>
Formtastic's :label_method option might help. E.g.
<%= form.input :movingworkers, :label_method => :worker %>
or
<%= form.input :movingworkers, :label_method => Proc.new { |x| "#{x.worker} #{x.quantity}" } %>
If the fields don't exist in the new view, you can just test if it is new (new_record?) and present a different set of fields (if you wrap into a partial in can be quite a clean approach).

Resources