I would like to know how to add / remove associations in a form. Lets say I have two models linked by foreign keys
class Event < ActiveRecord::Base
has_and_belongs_to_many :participants
end
class Participant < ActiveRecord::Base
has_and_belongs_to_many :events
end
I have created some forms to edit each model. However, is there a way to use the form to remove/add a participant from/to a lecture? (I guess this would require some javascript to add / remove entries in the view as well as well...)
What you are looking for is accepts_nested_attributes_for and some js magic. Ryan Bates describes this perfectly in some of his free episodes:
Nested Model Form Part 1
Nested Model Form Part 2
And here is the full source code of this episodes.
Maybe you'll have to adapt some things because it's a little old, for instance replace link_to_function for a simple link_to with an :onclick event, but I think pretty much everything else works on Rails 4.
Hope it helps!
Related
I'm creating an app that it needs to be customizable.
Ej:
I have:
Position scaffold
Category scaffold
Class scaffold
Type Scaffold
Now, the situation is, position table along other attributes, includes a category attr, a class attr and a type attr. The problem I have is that every category, class and type is created and owned by another controller and moedel.
Now, here is where my problem comes to life.
when I tried to create a position (even though I can actually see all categories, classes and types listed on my position view via form.select) I cannot save a position with a category, class nor type.
I tried nested_attributes and don't work quite good in this situation.
What is the appropriate method tho attack this from scratch base on my scaffolds?
Any help will be very much appreciated!
Thanks in advance!
It is a little tough to figure out what you need without seeing your code, but I'll try my best to help.
Keep in mind that Class and Type are reserved words in rails; Columns named _type are used for polymorphic relationships. And Class is used well for ruby classes. So as a general rule of thumb I would stay clear of those terms.
For you Position model:
class Position < ActiveRecord::Base
has_many :categories
has_many :types
has_many :classes
accepts_nested_attributes_for :categories, :types, :classes
end
Then for your other models:
class NameOfClass < ActiveRecord::Base
belongs_to :position
end
You also need to make sure that you add these nested attributes to the whitelist on you controllers. It should include any columns that you want to accept that will update other models. Keep in mind that this has to be the last thing listed.
In your Position Controller
PositionsController < ApplicationController
private
def position_params
params.require(:position).permit(:id, :position_column_a, :another_column,
categories_attributes: [:id, :position_id, :any_other_column_you_want],
types_attributes: [:id, :position_id, :again_any_other_column])
Again, before you do anything I would look into renaming your scaffolds. the best way is to delete them and then regenerate. This will do the trick:
rails d scaffold Type
If you just need a simple dropdown of a list of categories you can try enums. However, it is not good for options that need to be customizable for the user. I typically only used them for statuses.
https://bendyworks.com/blog/rails-enum-sharp-knife
You can also read the API docs: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I have the following:
a Link model
a LinkItem model, which I want to be of the following type
a comment
a tag
...
I am using this code:
Link model
class Link < ActiveRecord::Base
has_many :link_items
end
LinkItem model
class LinkItem < ActiveRecord::Base
belongs_to :link
end
class Comment < LinkItem
end
class Tag < LinkItem
end
Now I don't know how to tell Rails that my LinkItem model is supposed to be polymorphic. I've read the Rails Guide on asociations and other tutorials, but these just describe how to create a belongs_to association to multiple other models, not the other way around.
So my question would be:
How do I create a has_many association where the associated instances can be of different types? Or would it be better to create seperate models for comments, tags, etc. and just associate each of them individually with my Link model?
EDIT
Actually my code works.
I just tried using a 'type'-column (instead of 'link_item_type') in my database and rails automatically used it to save/determine the correct subclass of my LinkItems (thanks Wizard of Ogz for the hint)
However I still can't access the subclasses of LinkItem without referencing a LinkItem first. Is this some kind of lazyloading?
If you are looking for polymorphic association nicholaides has the right way .
If you are looking for has_meny polymorphic association , check out the answer to "Setting up a polymorphic has_many :through relationship".
This is called a polymorphic association. Here is some documentation.
I just dealt with what I think is the same issue.
My filename for my model was wrong. I initially created it with one name (ex. link_tag.rb), and then changed the name of the class (ex. from LinkTag to Tag) on the fly without changing the name of the file (ex. tag.rb).
When I renamed the file correctly, it worked as expected.
In summary, the name of the file needed to match the name of the class.
I know this post is a little old, but maybe that will help someone someday!
I user polymorphic associations a lot!
I would first watch this RailsCast and then the documentation suggested by nicholaides.
It perfectly explains how to create both sides of the association.
You have a set of related models created through a scaffold e.g. a house, which has many rooms, which each have many windows, which each has a selection of locks.
These resources are already full of data i.e. someone has entered all the information, such as: a room called 'kitchen' has various windows associated with it and these windows each have five different locks associated with them.
Someone comes along and says:
Can you create a form that lets someone create a new project where they can select the different rooms, windows and then specify the locks that they would like for that project? (these are already in the system, nothing new to add, just the associations to a new project)
This sounds like a nested form but I have wasted a lot of time trying to solve this - there are many levels of nesting, which make this tricky. Any suggestions?
session based solution
With such deeply nested models select box on the front end wouldn't be enough...
Assuming this, you may want to create a current_house who's id live in a session (just like current_user works).
Once you have your current_house add different items by navigating to your list of items view and clicking on the add_to link :
# house_controller.rb
def add_to
current_house.polymorphic_items << Kitchen.find(params[:id])
redirect_to :back
end
But there are many approaches to this session based solution which sort of implements a cart/order system. You may want to add a current_item to add stuff in each leaf of your tree aka room of your house.
E.G after clicking on the kitchen you just added :
before_filter :set_current_item
def add_to
current_item.windows << Window.find(id)
end
current_item beeing polymorphic : a living room, a bathroom etc.
But how you implement that precisely depends on your Domain Model....
As a rule of thumb regarding nested forms I'd follow rails guidance for routes : don't go deeper than one level or you'll end up in a mess.
Yes this is a nested form. Railscasts nested forms is a great place to start.
If everything is already in the system you probably just want select boxes so they can select what they want. Also check out the .build method. If you have multiple levels of nesting you can also manually set the association by passing in the foreign key yourself.
I think you can model this with a single level of nested attributes, given the models below (based on Windows/Locks pre-existing and a room just needing to mix and match them into a set of windows with given locks):
class House < ActiveRecord::Base
has_many :rooms
end
class Room < ActiveRecord::Base
belongs_to :house
has_many :window_configs
end
class WindowConfig < ActiveRecord::Base
belongs_to :room
belongs_to :window
belongs_to :lock
end
class Lock < ActiveRecord::Base
has_many :window_configs
end
class Window < ActiveRecord::Base
has_many :window_configs
end
... based on that model setup, you could have a single house form that you dynamically add child 'room' definitions to that each have a name and a collection of window_configs which have two select boxes for each one (choose a window definition and then a lock definition). Because you're dynamically adding multiple rooms with multiple windows, you'd need some JS to populate new form elements, but it could all live in a single nested form.
form_for :house do |form|
# Dynamically add a Room form for each room you want with js
form.fields_for :room do |room_attributes|
room_attributes.text_field :name
# Dynamically add window_config forms on Room w/ JS
room_attributes.fields_for :window_config do |window_attributes|
window_attributes.select :window_id, Window.all
window_attributes.select :lock_id, Lock.all
I've been trying to switch my Orders model to a polymorphic association with my Product and Service models. However, I have a few questions that I haven't been able to find answers to, even after watching the RailsCast and reading the documentation (so, those suggestions are appreciated, but I need a more concrete answer).
Question:
Is a polymorphic association the best thing to use in this case? Prior to this, I was using a Transaction model that had multiple belongs_to associations and used a custom Parent function to determine which one it was. This was working fine, but someone suggested a polymorphic association may clean things up.
I set up the polymorphic association properly and have been unable to have the transactable_id and transactable_type automatically populated. The code is below. I have side-stepped this by manually putting them in inside the form, but if anyone knows the proper way to do it, that would be great!
How can I access elements with polymorphic associations? For example, in my Cart object (which has_many Transactions and which Transactions belongs_to) I can no longer access things using #cart.transactions.each do |t| ... #t.product.name type coding.
My model associations look like this:
class Order < ActiveRecord::Base
belongs_to :orderable, :polymorphic => true
end
class Product < ActiveRecord::Base
has_many :orders, :as => :orderable
end
My forms used to look like this:
<% form_for [#orderable, #order] do |f| %>
...
<% end %>
And were rendered like this in my Product Show view:
<%= render 'orders/form' %>
Now, I pass a variable for the product.id in the render partial and use it to populate the transactable_id field. But, I feel like that is very messy.
Again, I have read the tutorials and API docs and have been unable to solve this, so any help would be greatly appreciated!!
Answers to your questions:
If your business login implies that multiple models will have related model with the same fields so you should use polymorphic association. (In your case you can use it).
If set up polymorphic association Rails will automatically handle setting *_id and *_type fields depending on associated parent model.
Lets say you have Product with many orders in polymorphic association and you want to define which model order belongs to:
order = Order.first
order.orderable
Suppose I have 3 models, Car, Motorcycle and Truck, and for each I have to enter a bunch of stuff, such as a list of known previous owners, traffic tickets, license plates, etc. So I created a model for each (PreviousOwners, PreviousPlates, etc) and set up polymorphic associations for the related models.
The problem is, how can I enter all of that using just one form, kind of like this:
Car #123
Known previous owners:
Jason Jazz
Brian Bass [add another]
Known previous license plates:
12345
67890 [add another]
Current status:
Cleared
(this is a dropdown select menu, CurrentStatus is also a polymorphic association, but with predefined values.)
etc
This is proving to be a bitch, way beyond my level of expertise (newbie here). The resources are not nested and almost everything I find on multiple models is for nested resources, and nothing seems to apply to polymorphic associations.
(This is just an example, I know ideally I should have a Vehicle model with 'Car', etc, as categories, but it's just to illustrate the real need for polymorphic models in my case.)
Thanks.
Maybe the PresenterPattern is helpfull too:
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
The basic idea is to create a presenter which acts like a model and processes all the incoming data from your form and distributes it to the models. This way it's also easy to create multiple instances of lets say PreviousOwner and attach it to Car.
Check the link out!
You can use the new nested attributes in Rails 2.3, but there is a certain way you have to write it to make it work. The trick is that you need to create the actual polymorphic object, then build the class that has the belongs to polymorphic clause in it. This is an example I found at Ryans Scraps, posted by a user named: Superslau (I've cleaned it up a good bit for here):
This feature is really awesome. I have
implemented this with polymorphic
associations, and it works!
class Task < ActiveRecord::Base
has_many :assets, :dependent=>:destroy
accepts_nested_attributes_for :assets, :allow_destroy => true
belongs_to :workable, :polymorphic => true
end
class Upload < ActiveRecord::Base
has_one :task, :as => :workable, :dependent=>:destroy
accepts_nested_attributes_for :task, :allow_destroy => true
end
Upload is a kind of task. All tasks
can have one or more assets uploaded.
I took me a while to figure out that I
should use the Upload model as the
parent. So in one form, I can create
an upload, and it’s corresponding task
entry, along with a file upload.
in my controller:
def new
#upload = Upload.new
#upload.task = Task.new
#upload.task.assets.build
end
Don’t
worry if that doesn’t make any sense,
I just wanted to let people know that
accepts_nested_attributes_for works
just fine with polymorphic
associations. Thanks Eloy!
Very well, nested form builders doesn't have to be associated with nested resources AFAIK.Can you post your models code as well?
There is a RailsCast on Complex Forms that might help you with building a single form from multiple models.
If the car/motorcycle/truck models are identical, you should add a type column to your vehicle model. If they're not, you should use STI (single table inheritance).
But yeah, need to see your models first before I can give you code.
You can avoid this and make things a bit simpler by introducing a Vehicle model. The Vehicle model can have all your PreviousOwners, PreviousPlates, etc collections, and then your Truck, Car and Motorcycle models can has_one Vehicle.