I have 2 models:
Video:
class Video < ActiveRecord::Base
belongs_to :user
has_many :thumbnails
attr_accessor :search, :saveable
accepts_nested_attributes_for :thumbnails, :allow_destroy => true
en
d
Thumbnail:
class Thumbnail < ActiveRecord::Base
belongs_to :video
end
I am using the YouTubeG gem in order to search for videos.
Each video that is returned by the search has a form in the view:
<% form_for :video, :url => videos_path, :html => { :class => :form } do |f| -%>
<%= f.hidden_field :url, :value => video.unique_id %>
<%= f.hidden_field :name, :value => video.title %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<% if video.thumbnails.present? %>
<% f.fields_for :thumbnails, video do |t| %>
<% video.thumbnails.each do |thumbnail| -%>
<%=image_tag thumbnail.url %>
<%=t.text_field :url, :value => thumbnail.url %>
<% end -%>
<% end -%>
<% end %>
<%= f.submit "Save" %>
<% end -%>
The f.fields_for :thumbnails produces
<input type="hidden" value="http://i.ytimg.com/vi/s8eigkwmMEo/0.jpg" name="video[thumbnails][url]" id="video_thumbnails_url"/>
which seems to wrong because I want to save all thumbnails for this video.
When I try to save I get
ActiveRecord::AssociationTypeMismatch in VideosController#create
Parameters:
{"commit"=>"Save",
"video"=>{"name"=>"Karajan - Beethoven Symphony No. 7",
"url"=>"s8eigkwmMEo",
"user_id"=>"1",
"thumbnails"=>{"url"=>"http://i.ytimg.com/vi/s8eigkwmMEo/0.jpg"}}} < there should be 4 thumbnails
I found the correct answer:
<% f.fields_for "thumbnails_attributes[]", Thumbnail.new do |t| %>
instead of
<% f.fields_for :thumbnails, video do |t| %>
You should use index feature of the fields_for helper:
<% video.thumbnails.each do |thumbnail| -%>
<% f.fields_for "thumbnail[]", thumbnail do |t| %>
<%=image_tag thumbnail.url %>
<%=t.text_field :url, :value => thumbnail.url %>
<% end -%>
<% end -%>
Look through rails casts edipode trilogy about complex forms:
Episode 1
Related
I'm working on a nested form where a user when creating a Screen should also attached an image (a separate model called Screenshot). I'm trying to validate the presence of the attachment before saving the new Screen to the db. I tried to validate the presence of the attachment in the Screenshot model, but that only precent Screenshot to be saved, while it still creates a Screen.
Here's my models:
class Screen < ActiveRecord::Base
belongs_to :project
has_many :screenshots
validates :name, presence: true
accepts_nested_attributes_for :screenshots
validates_presence_of :screenshots
end
class Screenshot < ActiveRecord::Base
belongs_to :screen
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
here's my controller:
def create
#screen = Screen.create(screen_params)
if #screen.save
flash[:notice] = "A new screen has been added to this project"
redirect_to [#screen.project]
else
render :action => 'new'
end
end
And lastly here's my form:
<%= form_for ([#project, #screen]), :html => { :multipart => true } do |f| %>
<% if #screen.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#screen.errors.count, "error") %> prohibited this screen from being saved:</h2>
<ul>
<% #screen.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :project_id %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :screenshots, #screen.screenshots.build do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any help to this noob is greatly appreciated.
Several things for you:
Form
You shouldn't build new objects in the form itself. That needs to happen in the controller's new function
<%= f.fields_for :screenshots do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
You can declare them like this:
def new
#screen = Screen.new
#screen.screenshots.build
#any more build declarations go here
end
Verification
I believe you should put the verification into the screnshots model, as the nested parameters are passed directly to the corresponding model. I'm not sure on that though.
In the screenshots model, I would put the validates_presence_of :screenshots, with the name as your Paperclip attachment's name
according to http://guides.rubyonrails.org/getting_started.html, I have below relationship models,
class Tag < ActiveRecord::Base
belongs_to :post
attr_accessible :name
end
class Post < ActiveRecord::Base
attr_accessible :context, :title, :tags_attributes
validates :title, :presence => true
validates :context, :presence => true, :length => {:minimum => 5}
has_many :comments
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc {|attrs| attrs.all? {|k,v| v.blank?} }
end
normally below code could work well when I sent a edit request,these existing tags are listed as editable elements on the page.
<%= form_for(#post) do |post_form| %>
<%= post_form.fields_for :tags do |tag_form|%>
<div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<div class="field">
<%= tag_form.label :_destroy, 'Remove:' %>
<%= tag_form.check_box :_destroy %>
</div>
<% end %>
<% end %>
but now,refer to below instance code on http://guides.rubyonrails.org/form_helpers.html
<%= form_for #person, :url => { :action => "create" } do |person_form| %>
<%= person_form.text_field :name %>
<%= fields_for #person.contact_detail do |contact_details_form| %>
<%= contact_details_form.text_field :phone_number %>
<% end %>
<% end %>
then I change the statement with fields_for to below format, why it always always prompt
undefined method `model_name' for Array:Class
<%= fields_for #post.tags do |tag_form|%>
at last, I make it work with below update
<% #post.tags.each do |tag| %>
<%= post_form.fields_for tags,tag do |tag_form|%>
<div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<div class="field">
<%= tag_form.label :_destroy, 'Remove:' %>
<%= tag_form.check_box :_destroy %>
</div>
<% end %>
<% end %>
<% end %>
I am trying to use the accepts_nested_attributes_for method within my model, however I need to render the records grouped by another association. I have got this to work but the method I have used seems like a bit of a hack.
Is there a better way to structure this?
My Model
has_many :quantities
has_many :ingredients, :through => :quantities, :uniq => true
has_many :sizes, :through => :quantities, :uniq => true
has_many :photos, :as => :imageable
accepts_nested_attributes_for :quantities
My View
<%= form_for [:admin, #recipe] do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<% #recipe.quantities.group_by(&:size).each do |size, quantities| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities do |builder| %>
<% if builder.object.size == size %>
<p>
<%= builder.text_area :value, :rows => 1 %>
</p>
<% end %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You can get rid of the if builder.object.size == size part with this :
<% #recipe.quantities.group_by(&:size).each do |size, quantities_for_size| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities, quantities_for_size do |builder| %>
<p><%= builder.text_area :value, :rows => 1 %></p>
<% end %>
<% end %>
passing the quantities_for_size as a second argument to fields_for should make it use it instead of the whole quantities associated to the recipe. See the docs on #fields_for for more information.
I'm getting this error:
Can't mass-assign protected attributes: quantities
I looked up all the threads concerning this issue in the site, but couldn't find something to answer my problem. Here are the code snippets:
product.rb
class Product < ActiveRecord::Base
attr_accessible :name, :quantities_attributes
has_many :quantities
accepts_nested_attributes_for :quantities, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
new.html.erb
<% if #was_submitted %>
<%= form_for(:new_product_array, :url => products_path) do |f| %>
<% prefix ||= 0 %>
<% #new_product_array.each do |n| %>
<% n.quantities.build %>
<% prefix += 1 %>
<%= f.fields_for(prefix.to_s ) do |child| %>
<div class="field">
<%= child.label :name %><br />
<%= child.text_field :name%>
</div>
<%= render :partial => 'quantities/form',
:locals => {:form => child} %>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag :submit %>
</div>
<% end %>
<% else %>
<%= form_tag new_product_path, :method => 'get' do %>
<p align=center>
How many Items are you Adding? (1-100)
<%= number_field_tag 'amount', 1, :in => 1...100 %>
</br>
To which storage?
<%= number_field_tag 'storage', 1, :in => 1...100 %>
<%= submit_tag "Next", :name => 'submitted' %>
</p>
<% end %>
<% end %>
<%= link_to 'Back', products_path %>
product_controller.rb
def new
#product = Product.new
if params['submitted']
#was_submitted = true
#amount_form = params['amount']
#new_product_array = []
(1..#amount_form.to_i).each do
#new_product_array << Product.new
end
#storage_form = params['storage']
else
#was_submitted = false
end
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #product }
end
end
def create
i=0
logger.info params[:new_product_array].inspect
params[:new_product_array].each do |new_product|
if new_product.last[:name] != nil
#new_product_array[i] = Product.new(new_product.last)
#new_product_array[i].save
i+=1
end
end
redirect_to(products_path)
end
quantity.rb
class Quantity < ActiveRecord::Base
belongs_to :product
attr_accessible :amount, :storage
end
quantity/_form.html.erb
<%= form.fields_for :quantities do |quant| %>
<div class="field">
<%= quant.label :storage %><br />
<%= quant.number_field :storage %>
</div>
<div class="field">
<%= quant.label :amount %><br />
<%= quant.number_field :amount %>
</div>
<% unless quant.object.nil? || quant.object.new_record? %>
<div class="field">
<%= quant.label :_destroy, 'Remove:' %>
<%= quant.check_box :_destroy %>
</div>
<% end %>
<% end %>
Overall what Im trying to do, is ask the user how much products to add, then make a form with the number of fields the user specifies and with one submit button add all of the products, whereas when you add a product you also add a quantity record which holds more information on the product.
You need a line like this:
attr_accessible :name, :quantities_attributes, :quantities
You have very bad code, it can be much simplier 100%.
Your problem is that form dont' know nothing about your resource (product), so it can't render 'smartly' fields "quantities_attributes", it renders "quantities" instead.
My current goal in my first rails project is to have a button that will create a #my_tea using the attributes of a #tea (show page). This is the error I am getting:
'undefined method `my_teas_path' for #<#:0xa578cf8>
I have tried having the form in a _new partial inside my_teas/ and inside teas/_add_tea both have given me the same error. Anyway here is my code as it stands.
View:
<%= form_for([#user, #my_tea]) do |f| %>
<%= f.hidden_field :name, :value => #tea.name %>
<%= f.hidden_field :tea_type, :value => #tea.tea_type %>
<%= f.hidden_field :store, :value => #tea.store %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= fields_for [#user, #tea_relationship] do |r| %>
<%= r.hidden_field :tea_id, :value => #tea.id %>
<% end %>
<%= f.submit "Add Tea", class: "btn btn-large btn-primary" %>
<% end %>
my_tea controller
def new
#my_tea = MyTea.new
end
def show
#my_tea = MyTea.find(params[:id])
end
def create
#my_tea = MyTea.new(params[:my_tea])
if #my_tea.save
flash[:success] = "Tea added to your teas!"
else
redirect_to user_path
end
end
Teas controller:
def show
#tea = Tea.find(params[:id])
#my_tea = MyTea.new
#tea_relationship = TeaRelationship.new
end
Routes
resources :users do
resources :my_teas
end
resources :teas
Models:
class User < ActiveRecord::Base
has_many :my_teas, :dependent => :destroy
has_many :tea_relationships, :dependent => :destroy
class MyTea < ActiveRecord::Base
belongs_to :user
class TeaRelationship < ActiveRecord::Base
belongs_to :user, class_name: "User"
end
Tea model doesn't belong to anything.
Please help rails community your my only hope :p
Update
changing my form to this
<%= form_for([#user, #my_tea]) do |f| %>
<%= f.hidden_field :name, :value => #tea.name %>
<%= f.hidden_field :tea_type, :value => #tea.tea_type %>
<%= f.hidden_field :store, :value => #tea.store %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= fields_for #tea_relationship do |r| %>
<%= r.hidden_field :tea_id, :value => #tea.id %>
<% end %>
<%= f.submit "Add Tea", class: "btn btn-large btn-primary" %>
<% end %>
it works and the #my_tea submits but the #tea_relationship doesn't.
So by the look of things and glimpsing through, it seems like you are trying to do some nested forms. It also appears like you have a many-to-many relationship( tea.rb <=> tea_relationship.rb <=> my_tea.rb) Make sure your Models are set up correctly.
Many to Many
I am not sure why you are trying to do [#user, #my_tea]
Nested Forms
Should be more in the lines of
<%= form_for #my_tea, :url => posting_path do |f| %>
<%= f.simple_fields_for :teas, #my_tea.teas.build do |x| %>
...
<%end%>
...
<%end%>
hope that helps!