Rails nested form attributes not getting saved - ruby-on-rails

I've already looked through every other stackoverflow for this issue, but none of the solutions have fixed this. My elements in a nested_form are not being saved in the database. I've also made sure that all model associations are correct. I've been trying to fix this for nearly 8 hours now, and would really appreciate some help, especially considering every other solution hasn't worked.
Basically, I have a Playlist model that contains multiple Song models. I'm trying to use a nested_form to add the Song models to the Playlist. However, none of the Songs are ever being saved. I apologize if my methods are misguides, as I'm still fairly new to Rails.
GitHub Repo:https://github.com/nsalesky/Ultra-Music
playlists_controller.rb
def index
#user = current_user
#playlists = #user.playlists
end
def show
#user = current_user
#playlist = #user.playlists.find(params[:id])
end
def new
#playlist = Playlist.new
#I was told to do this
#playlist.songs.build
end
def create
#user = current_user
#playlist = #user.playlists.create(playlist_params)
if #playlist.save
redirect_to #playlist
else
render :action => 'new'
end
end
def edit
#playlist = current_user.playlists.find(params[:id])
end
def update
#user = current_user
#playlist = #user.playlists.find(params[:id])
if #playlist.update_attributes(playlist_params)
redirect_to #playlist
else
render :action => 'edit'
end
end
def destroy
#user = current_user
#playlist = #user.playlists.find(params[:id])
#playlist.destroy
redirect_to playlists_path(#user.playlists)
end
private
def playlist_params
params.require(:playlist).permit(:name, :description, songs_attributes: [:id, :name, :link, :_destroy])
end
playlist.rb
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, :allow_destroy => true, :reject_if => lambda { |a| a[:content].blank? }
validates :name, presence: true
validates_associated :songs, presence: true
_form.html.erb
<%= nested_form_for #playlist do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<!--<div>
<button type="button" id="addsong">Add Song</button><br>
<button type="button" id="removesong">Remove Song</button><br>
</div> !-->
<div>
<%= f.fields_for :songs do |song_form| %>
<%= song_form.text_field :name %>
<%= song_form.text_field :link %>
<%= song_form.link_to_remove "Remove Song" %>
<% end %>
<p><%= f.link_to_add "Add Song", :songs %></p>
</div>
<div>
<%= f.submit %>
</div>
<% end %>

In your playlist.rb, you wrote:
:reject_if => lambda { |a| a[:content].blank? }
Here the block parameter |a| stands for attributes of a specific song. So a[:attribute] relates to a single attribute. The problem is your Song doesn't have a :content attribute. So this a[:content].blank? will always be true, means you would be rejected building a song.
Just change a[:content] to a valid attribute such as a[:name]

Related

Rails - Use fields_for to create multiple nested objects

Hoping someone can help out with this. I have two models order and date_order. Each order can have multiple date_orders, and I should be able to create many date_orders as I create an order.
How do I do that? As you can see, my code is working well for creating ONE date_order and relating it to the created order.
UPDATE: I have tried to create many "builders" in my orders/new file. It worked on the view, and created an order when I entered multiple dates and times. But the fields_for did not create any date_orders.
orders_controller.rb
def new
#order = Order.new
#order.date_orders.build
end
def create
#order = Order.new(order_params)
if #order.save
flash[:success] = "blah"
redirect_to #order
else
render 'new'
end
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
date_orders_attributes: [:id, :order_date, :time_start, :time_end, :order_id])
end
order.rb
class Order < ActiveRecord::Base
has_many :date_orders, :dependent => :destroy
accepts_nested_attributes_for :date_orders, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
date_order.rb
class DateOrder < ActiveRecord::Base
belongs_to :order
end
order/new.html.erb
<%= form_for(#order, :html => {:multipart => true}) do |f| %>
## SOME QUESTIONS ##
<%= f.fields_for :date_orders do |builder| %>
<%= builder.label :date %>
<%= builder.date_field :order_date %>
<%= builder.label :starting_time %>
<%= builder.time_field :time_start %>
<%= builder.label :ending_time %>
<%= builder.time_field :time_end %>
<% end %>
<% end %>
Build more orders_dates:
class OrdersController < ApplicationController
def new
#order = Order.new
5.times { #order.date_orders.build } # < === HERE ===
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
# |- === HERE ===
date_orders_attributes: [:id, :content, :order_date, :time_start, :time_end, :order_id])
end
end
Update:
Also, add content to your strong params whitelist.

acts as commentable: comment body formatting

I think I have a working version of acts_as_commenting_with_threading in my rails app, but it seems like the body of every comment is saved with weird formatting. How do I remove the formatting in my view so it only displays the text (and not the formatting)? For example, if I type the text "test comment," the body of the comment is saved as "---\nbody: test comment\n". I tried html_safe, but it didn't work.
step.rb
class Step < ActiveRecord::Base
extend FriendlyId
acts_as_commentable
friendly_id :position
has_ancestry :orphan_strategy => :adopt
attr_accessible :description, :name, :position, :project_id, :images_attributes, :parent_id, :ancestry, :published_on
belongs_to :project
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :allow_destroy => :true
validates :name, :presence => true
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#project = Project.find(params[:project_id])
#commentText = params[:comment]
#user = current_user
#comment = Comment.build_from(#project.steps.find(params[:step_id]), #user.id, #commentText)
respond_to do |format|
if #comment.save
format.html {redirect_to :back}
else
format.html { render :action => 'new' }
end
end
end
end
show.html.erb:
<div class="stepComments">
<% if step.comment_threads.count >0 %>
<% step.comment_threads.each do |stepComment| %>
<% if stepComment.body.length>0 %>
<%= render :partial => 'comments', :locals => {:comment=> stepComment} %>
<% end %>
<br>
<% end %>
<% end %>
</div>
_comments.html.erb
<div class="comment">
<div class="userIcon">
<%= User.find(comment.user_id).username %>
<%= image_tag(User.where(:id=>comment.user_id).first.avatar_url(:thumb), :class=>"commentAvatar img-polaroid")%>
</div>
<div class="field">
<%= comment.body %>
</div>
</div>
This prints: "---\nbody: test comment\n"
The rails helper simple_format will print using the formatting rules so you will get just the text.
For example, <% simple_format(comment.body) %>
I couldn't figure out a way to do it besides just edited the string manually. This is what I ended up using:
<%= comment.body.slice((comment.body.index(' ')+1..comment.body.length)) %>
It seems very odd that there isn't some built in function for doing this...
It ended up being a quite simple solution; I had been calling the parameter incorrectly. It should have been:
#commentText = params[:comment][:body]

Rails 3: Couldn't find User without an ID

Hi I'm currently working on my first rails project (a photo uploading site), and am getting an error on a form that creates a new photo album that belongs to a user. I'm trying to use the Paperclip gem. It's giving me the error:
ActiveRecord::RecordNotFound in AlbumsController#create
Couldn't find User without an ID
I'm guessing it has something to do with the instance variables in my create action, but I don't see what's wrong. Any guidance would be helpful. Here are my files:
_form.html.erb
<%= form_for (#album), :remote => true, :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.fields_for :avatar do |avatar_form| %>
<%= avatar_form.label :avatar, "Upload" %>
<%= avatar_form.file_field :avatar, :multiple => true %>
<% end %>
<%=f.submit %>
</div>
<% end %>
albums_controller create action:
def create
#users = User.all
#user = User.find(params[:user_id])
#album = #user.albums.build(params[:album])
if #album.save
flash[:success] = "Album created!"
end
end
config/routes
Pholder::Application.routes.draw do
resources :users do
resources :albums
end
resources :albums do
resources :pictures
end
Album model
class Album < ActiveRecord::Base
attr_accessible :avatar, :name, :description
has_and_belongs_to_many :users
has_attached_file :avatar
end
Let me know if you need to see any other files.
Since this is a new album, there is no user_id in the albums hash as I first thought. I added a new method that merges in the user id to the hash.
def create
#users = User.all
#album = Album.new(params[:album].merge!(:user_id => current_user))
if #album.save
flash[:success] = "Album created!"
end
end
If you would rather not have user_id in attr_accessible in your Album model, you can assign it separately.
def create
#users = User.all
#album = Album.new(params[:album])
#album.user_id = current_user.id
if #album.save
flash[:success] = "Album created!"
end
end

How to add has_many :through details to a form RoR

I'm doing what appears to be a common learning app for Ruby on Rails, the recipe app. Specifically, working on recipes and ingredients as a has_many :through relationship. Through looking at a million examples and questions, I've got my many-to-many relationship setup and my multi-model form working, but I'd like to add an additional field and can't get it working. Feels like I'm close to understanding how this stuff works. Here are the quick details:
Models:
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, :through => :recipe_ingredients
end
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
accepts_nested_attributes_for :ingredients, :recipe_ingredients
def new_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredient_attributes.each do |attributes|
recipe_ingredients.build(attributes)
end
end
def existing_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredients.reject(&:new_record?).each do |recipe_ingredient|
attributes = recipe_ingredient_attributes[recipe_ingredient.id.to_s]
if attributes
recipe_ingredient.attributes = attributes
else
recipe_ingredient.delete(recipe_ingredient)
end
end
end
def save_recipe_ingredients
recipe_ingredients.each do |recipe_ingredient|
recipe_ingredient.save(false)
end
end
end
Controller:
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your record has been saved."
else
render :action => 'new'
end
end
def update
params[:recipe][:existing_recipe_ingredient_attributes] ||= {}
#recipe = Recipe.find(params[:id])
if #recipe.update_attributes(params[:recipe])
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your changes have been saved."
else
render :action => 'edit'
end
end
View:
<% form_for(#recipe) do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %>
etc.....
Ingredients:
<div id="recipe_ingredients">
<div class="recipe_ingredient">
<% new_or_existing = recipe_ingredient.new_record? ? 'new' : 'existing' %>
<% prefix = "recipe[#{new_or_existing}_recipe_ingredient_attributes][]" %>
<% fields_for prefix, recipe_ingredient do |ri_form| %>
<p>
<%= ri_form.collection_select(:id, Ingredient.find(:all), :id, :name, :include_blank => true) %>
<%= ri_form.text_field :amount %>
</p>
<% end %>
</div>
</div>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
Sorry for the wall of code, hopefully it makes sense. The thing I can't understand is why the "amount" text field doesn't work. I've tried a million different ways, but can't get it working. In this case, the error I get is "undefined method `amount' for #"
What key connection am I missing here? Thanks.
At first glance it appears you should simply replace:
<% fields_for prefix, recipe_ingredient do |ri_form| %>
with:
<%= fields_for prefix, recipe_ingredient do |ri_form| %>

rails 3, paperclip (& formtastic) - deleting image attachments

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

Resources