In my Rails 3.0 app I have a Client model and a polymorphic Addresses model. As per the code below a client can have many addresses. I would like to my form to update a single client address at a time. I can only seem to get the [addresses_attributes] to appear if I allow all of the client's addresses to be edited at the same time. Is there a way around this?
class Client < ActiveRecord::Base
has_many :addresses, :as => :addressable, :dependent => :destroy
accepts_nested_attributes_for :addresses
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
Clients Controller
def edit
#client = Client.find(params[:id])
#addresses = #client.addresses
if params[:address]
#address = #client.addresses.find(params[:address])
else
#addresses ? #address = #addresses.first : #address = []
end
end
def update
#client = Client.find(params[:id])
#client.update_attributes(params[:client])
redirect_to client_path(#client)
end
View
<%= form_for #client do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= f.fields_for #address do |addresses_attributes| %>
<%= render :partial => 'addresses/fields', :locals => {:f => addresses_attributes} %>
<% end %>
<%= f.submit %>
<% end %>
EDIT:
Sorry, I reread the post and realized that there were better options. You should add a scope to the Address model or you should create separate associations in the Client using conditions.
http://railscasts.com/episodes/108-named-scope
or see conditions here:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
DETAIL:
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
named_scope :type_one, :conditions => { :address_type => 'one' }
named_scope :type_two, :conditions => { :address_type => 'two' }
<%= f.fields_for #address.type_one do |addresses_attributes| %>
<%= render :partial => 'addresses/fields', :locals => {:f => addresses_attributes} %>
<% end %>
Related
Maybe someone could tell me how to create or correctly pass params from partial form_for to custom controller? Here is what i got so far:
Basically there are two classes: Scribbles (polymorphic - connected to local feeds) and local feeds. Trough command line, I can create scribbles, assign them to local feeds and display them in html. But creating them in trough different controller seems to be a rather tricky task. Any help would be appreciated.
Scribble.rb
class Scribble < ActiveRecord::Base
attr_accessible :post, :comments_attributes, :user_id, :posted_by, :localfeed_attributes
belongs_to :scribbled, :polymorphic => true
has_many :comments, :as => :commentable
accepts_nested_attributes_for :comments
end
localfeed.rb
class Localfeed < ActiveRecord::Base
attr_accessible :city, :scribble_id, :location_id, :localfeed_id, :scribble_attributes
belongs_to :location
has_many :scribbles, :as => :scribbled
accepts_nested_attributes_for :scribbles
validates :city, :presence => true, :uniqueness => true
Here is the error
NoMethodError in LocalfeedsController#newlocalscribble
undefined method `scribbles' for nil:NilClass
Rails.root: c:/workspace/uu2
Application Trace | Framework Trace | Full Trace
app/controllers/localfeeds_controller.rb:80:in `newlocalscribble'
localfeeds/show.erb.html
<% #newlocalscribble = #localfeed.scribbles.new %>
<%=render :partial => 'newlocalscribble.html.erb', :locals => {:newlocalscribble => #newlocalscribble, :localfeed => #localfeed}%>
localfeeds/_newlocalscribble.html.erb
<%= form_for #newlocalscribble, :remote => true, :url => url_for(:controller => 'localfeeds', :action => 'newlocalscribble') do |f| %>
<div class="">
<div class="field">
<%= f.text_area :post,:rows=>3,:placeholder=>"What's on your mind,#{current_user.full_name}?", :class=>"sribble-status-text" %>
<%= f.hidden_field :localfeed, :value => #localfeed.id%>
</div>
<div class="sribble-status-actions" id="newlocalscribble">
<%= f.submit "Share",:class=>"btn btn-info" %>
</div>
</div>
<% end %>
localfeeds_controller.rb
def newlocalscribble
#localfeed = Localfeed.find_by_id(params[:localfeed])
#user = current_user
#newlocalscribble = #localfeed.scribbles.create(params[:localscribble])
##localscribble.scribbled = #user
##localscribble.scribbled = #localfeed
end
def new
#localfeed = Localfeed.new
#feed = Localfeed.find_by_id(params[:localfeed])
#newlocalscribble = #feed.scribbles.new
end
routes.rb
resources :localfeeds do
resource :scribbles
collection do
post 'localscribble', :action => :newlocalscribble
end
end
Variables passed.
{"utf8"=>"✓",
"authenticity_token"=>"1+/Qu/o4EeEbpiL/g07XFa3756IQDo6ldmKH196EkSQ=",
"scribble"=>{"post"=>"hfdfs",
"localfeed"=>"1"},
"commit"=>"Share"}
most likely your variable params[:localfeed] is not set right. It is either nil or the value that came in is not in the database.
Solved It by passing the :locals => {:scribble => #scribble, :localfeed => #localfeed} to pass the right feed ID params to partial, and deleted the hidden field which created a duplicate and cause mass assignment error
Thank you Guys
I want to use multiple select drop-down between several models
I have this:
class Report < ActiveRecord::Base
belongs_to :region
end
class City < ActiveRecord::Base
has_many :regions
end
class Region < ActiveRecord::Base
has_many :reports
belongs_to :city
end
when I select a city I want to pull list of items from selected city and show it on next drop-down list. How do I create relationships between drop-down menus ? Can anyone help me?
Thanks.
This link may help you in doing it.
Rails 2 + Prototype
app/models/cities.rb
class City < ActiveRecord::Base
has_many :regions
has_many :reports, :through => :regions # this is newly added
end
cities_controller
class CitiesController < ApplicationController
def index
#cities = City.find(:all)
#regions = Region.find(:all)
#reports = Report.find(:all)
end
def update_regions
# updates regions and reports based on city selected
city = City.find(params[:city_id])
regions = city.regions
reports = city.reports
render :update do |page|
page.replace_html 'regions', :partial => 'regions', :object => regions
page.replace_html 'reports', :partial => 'reports', :object => reports
end
end
def update_reports
# updates reports based on region selected
region = Region.find(params[:region_id])
reports = region.reports
render :update do |page|
page.replace_html 'reports', :partial => 'reports', :object => reports
end
end
end
_reports.html.erb
<%= collection_select(nil, :report_id, reports, :id, :title,
{:prompt => "Select a Report"}) %>
_regions.html.erb
<%= collection_select(nil, :region_id, regions, :id, :name,
{:prompt => "Select a Region"},
{:onchange => "#{remote_function(:url => {:action => "update_reports"},
:with => "'region_id='+value")}"}) %>
<br/>
index.html.erb
<%= javascript_include_tag :defaults %>
<%= collection_select(nil, :city_id, #cities, :id, :name,
{:prompt => "Select a City"},
{:onchange => "#{remote_function(:url => {:action => "update_regions"},
:with => "'city_id='+value")}"}) %>
<br/>
<div id="regions"><%= render :partial => 'regions', :object => #regions %></div>
<div id="reports"><%= render :partial => 'reports', :object => #reports %></div>
I'm trying to get it to work but it dosen't!
I have
class User < ActiveRecord::Base
has_many :events, :through => :event_users
has_many :event_users
accepts_nested_attributes_for :event_users
end
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, :through => :event_users
accepts_nested_attributes_for :users
end
class EventUser < ActiveRecord::Base
set_table_name :events_users
belongs_to :event
belongs_to :user
accepts_nested_attributes_for :events
accepts_nested_attributes_for :users
end
And also the table-layout
event_users
user_id
event_id
user_type
events
id
name
users
id
name
And this is my form
<%= semantic_form_for #event do |f| %>
<%= f.semantic_fields_for :users, f.object.users do |f1| %>
<%= f1.text_field :name, "Name" %>
<%= f1.semantic_fields_for :event_users do |f2| %>
<%= f2.hidden_field :user_type, :value => 'participating' %>
<% end %>
<% end %>
<%= link_to_add_association 'add task', f, :users %>
<% end %>
The problem is that if I create a new user this way, it doesn't set the value of user_type (but it creates a user and a event_users with user_id and event_id). If I go back to the edit-form after the creation of a user and submit, then the value of user_type is set in events_users. (I have also tried without formtastic)
Any suggestions? Thanks!
----edit----
I have also tried to have the event_users before users
<%= semantic_form_for #event do |f| %>
<%= f.semantic_fields_for :event_users do |f1| %>
<%= f1.hidden_field :user_type, :value => 'participating' %>
<%= f1.semantic_fields_for :users do |f2| %>
<%= f2.text_field :name, "Name" %>
<% end %>
<% end %>
<%= link_to_add_association 'add task', f, :event_users %>
<% end %>
but then it only throws me an error:
User(#2366531740) expected, got
ActiveSupport::HashWithIndifferentAccess(#2164210940)
--edit--
the link_to_association is a formtastic-cocoon method (https://github.com/nathanvda/formtastic-cocoon) but I have tried to do other approaches but with the same result
---edit----
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
format.html { redirect_to(#event, :notice => 'Event was successfully created.') }
format.xml { render :xml => #event, :status => :created, :location => #event }
else
format.html { render :action => "new" }
format.xml { render :xml => #event.errors, :status => :unprocessable_entity }
end
end
end
To be honest, i have never tried to edit or create a has_many :through in that way.
It took a little while, and had to fix the js inside formtastic_cocoon to get it working, so here is a working solution.
You need to specift the EventUser model, and then fill the User model (the other way round will never work).
So inside the models you write:
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, :through => :event_users
accepts_nested_attributes_for :users, :reject_if => proc {|attributes| attributes[:name].blank? }, :allow_destroy => true
accepts_nested_attributes_for :event_users, :reject_if => proc {|attributes| attributes[:user_attributes][:name].blank? }, :allow_destroy => true
end
class EventUser < ActiveRecord::Base
belongs_to :user
belongs_to :event
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
has_many :events, :through => :event_users
has_many :event_users
end
Then the views. Start with the events/_form.html.haml
= semantic_form_for #event do |f|
- f.inputs do
= f.input :name
%h3 Users (with user-type)
#users_with_usertype
= f.semantic_fields_for :event_users do |event_user|
= render 'event_user_fields', :f => event_user
.links
= link_to_add_association 'add user with usertype', f, :event_users
.actions
= f.submit 'Save'
(i ignore errors for now)
Then, you will need to specify the partial _event_user_fields.html.haml partial (here comes a little bit of magic) :
.nested-fields
= f.inputs do
= f.input :user_type, :as => :hidden, :value => 'participating'
- if f.object.new_record?
- f.object.build_user
= f.fields_for(:user, f.object.user, :child_index => "new_user") do |builder|
= render("user_fields", :f => builder, :dynamic => true)
and to end the _user_fields partial (which does not really have to be a partial)
.nested-fields
= f.inputs do
= f.input :name
This should work.
Do note that i had to update the formtastic_cocoon gem, so you will need to update to version 0.0.2.
Now it would be easily possible to select the user_type from a simple dropdown, instead of a hidden field, e.g. use
= f.input :user_type, :as => :select, :collection => ["Participator", "Organizer", "Sponsor"]
Some thoughts (now i proved it works):
this will always create new users on the fly, actually eliminating the need for the EventUser. Will you allow selecting existing users from a dropdown too?
personally i would turn it around: let users assign themselves to an event!
Does the events_users model not have an ID column? Since there's an additional field (user_type) then EventUser is a model and should probably have an ID. Maybe that's why user_type isn't being set in your first case.
I am trying to run this..
- f.fields_for :referrals do |qf|
But I would like to pass this, #organization.referrals.select{|ref|ref.new_record?} as well. This is so that the forms passed are exclusively new objects, and not older ones.
I've tried to do this..
- f.fields_for :referrals do |qf|
- if qf.object.new_record?
= render :partial => 'referral_fields', :locals => {:qf => qf}
Which makes it display correctly in the view, but the params are still populated with every single previously created nested object.
Which leads me to believe that I need to pass this option within the fields_for statement itself.
I have also tried this :
- f.fields_for #organization.referrals.select{|ref|ref.new_record?} do |qf|
= render :partial => 'referral_fields', :locals => {:qf => qf}
As well as this :
- f.fields_for :referrals, #organization.referrals.select{|ref|ref.new_record?} do |qf|
= render :partial => 'referral_fields', :locals => {:qf => qf}
The first example will show and only allows to pass one object. Where as my form is to allow a dynamic number of duplicate nested forms.
The second will display and pass all nested objects
App Info
#organization.rb
has_many :referrals
accepts_nested_attributes_for :referrals, :reject_if => proc { |attributes| attributes.all? { |k, v| v.blank? } }, :allow_destroy => true
#referral.rb
belongs_to :organization
#referrals_controller.rb
def new
2.times { #organization.referrals.build }
....
def create
#referral = Referral.new(params[:referral])
if #referral.valid? && #organization.referrals << #referral
flash[:notice] = "Referrals saved."
redirect_to new_organization_referrals_path(#organization)
else
render :action => :new, :layout => 'manage'
end
end
Here's what you want:
- f.fields_for :referrals, #organization.referrals.select{|ref|ref.new_record?} do |qf|
= render :partial => 'referral_fields', :locals => {:qf => qf}
The first parameter is the association name, which rails needs in order to know how to structure the params. If your first parameter is a collection, rails can usually infer the association name from that collection.
Your collection however, has been filtered into a regular array, where the association can't be as easily inferred. So you pass the specific collection as the second parameter.
Good luck!
UPDATE
I've built out a small rails app to analyze the problem, and the solution above is working just fine for me - the edit form doesn't display existing referrals, only new ones. I'll post the relevant code, so we can see where you and I might differ. One caveat, this is all in erb since I rarely work with haml and wouldn't want a typo to mess up the solution :)
My models:
# app/models/organization.rb
class Organization < ActiveRecord::Base
has_many :referrals
accepts_nested_attributes_for :referrals
end
# app/models/referral.rb
class Referral < ActiveRecord::Base
belongs_to :organization
end
My controller's edit action:
# app/controllers/organizations_controller.rb
class OrganizationsController < ApplicationController
def edit
#organization = Organization.find(params[:id])
2.times { #organization.referrals.build }
end
end
My views:
# app/views/organizations/edit.html.erb
<h1>Editing <%= #organization.name %></h1>
<% form_for(#organization) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :referrals, #organization.referrals.select{|ref| ref.new_record?} do |referral_fields| %>
<%= render :partial => 'referral', :locals => {:f => referral_fields} %>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
# app/views/organizations/_referral.html.erb
<p>
<%= f.label :name, 'Referral Name' %><br />
<%= f.text_field :name %>
</p>
Of course, I just read your new comments, and maybe you don't need this anymore. Oh well, more documentation for posterity :)
I can't seem to find an example that is complete in all the components. I am having a hard time deleting image attachments
Classes
class Product
has_many :product_images, :dependent => :destroy
accepts_nested_attributes_for :product_images
end
class ProductImage
belongs_to :product
has_attached_file :image #(etc)
end
View
<%= semantic_form_for [:admin, #product], :html => {:multipart => true} do |f| %>
<%= f.inputs "Images" do %>
<%= f.semantic_fields_for :product_images do |product_image| %>
<% unless product_image.object.new_record? %>
<%= product_image.input :_destroy, :as => :boolean,
:label => image_tag(product_image.object.image.url(:thumb)) %>
<% else %>
<%= product_image.input :image, :as => :file, :name => "Add Image" %>
<% end %>
<% end %>
<% end %>
<% end %>
Controller
class Admin::ProductsController < AdminsController
def edit
#product = Product.find_by_permalink(params[:id])
3.times {#product.product_images.build} # added this to create add slots
end
def update
#product = Product.find_by_permalink(params[:id])
if #product.update_attributes(params[:product])
flash[:notice] = "Successfully updated product."
redirect_to [:admin, #product]
else
flash[:error] = #product.errors.full_messages
render :action => 'edit'
end
end
end
Looks good, but, literally nothing happens when I check the checkbox.
In the request I see:
"product"=>{"manufacturer_id"=>"2", "size"=>"", "cost"=>"5995.0",
"product_images_attributes"=>{"0"=>{"id"=>"2", "_destroy"=>"1"}}
But nothing gets updated and the product image is not saved.
Am I missing something fundamental about how 'accepts_nested_attributes_for' works?
From the API docs for ActiveRecord::NestedAttributes::ClassMethods
:allow_destroy
If true, destroys any members from the attributes hash with a _destroy key and a value that evaluates to true (eg. 1, ‘1’, true, or ‘true’). This option is off by default.
So:
accepts_nested_attributes_for :product_images, allow_destroy: true