Rails4 and Cocoon - How to Handle Has_One Relationship - ruby-on-rails

I'm trying to deal with nested forms in my Rails4 application but I'm having problems with has_one relationship because all the GEM documentation is only for has_many. When I try to create a new examination the app is throwing "Unpermitted parameters: exam_statuses" message. Probably I'm doing something wrong with singular&plural names but can you please check what is wrong with this code. Here is my codes;
examination.rb:
class Examination < ActiveRecord::Base
has_one :exam_status, dependent: :destroy
accepts_nested_attributes_for :exam_status, :reject_if => :all_blank, :allow_destroy => true
end
examination_controller.rb:
.....
def new
#examination = Examination.new
end
def create
#examination = Examination.new(examination_params)
respond_to do |format|
if #examination.save
format.html { redirect_to examinations_path, notice: 'success' }
else
format.html { render action: 'new' }
end
end
end
private
def set_examination
#examination = Examination.find(params[:id])
end
def examination_params
params.require(:examination).permit(:name, :shortname, :fee, :exam_status => [:first_application_date, :last_application_date, :examination_id])
end
views/examinations/new.html.erb =>
<%= simple_form_for(#examination) do |f| %>
<%= f.input :name %>
<%= f.input :shortname %>
<%= f.input :fee %>
<%= f.simple_fields_for :exam_status do |exam_status| %>
<%= render 'exam_status_fields', :f => exam_status %>
<% end %>
<% end %>
views/examinations/_exam_status_fields.html.erb =>
<div class="nested-fields">
<%= f.input :first_application_date, as: :datetime %>
<%= f.input :last_application_date, as: :datetime %>
<%= f.input :examination_id %>
</div>
Log File =>
Started POST "/examinations" for 127.0.0.1 at 2014-07-03 16:43:30 +0300
Processing by ExaminationsController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"uYpiZ9Z1txaJ1mib+NEAG7Ckwm9F3TyNQ6jRpYz3ncA=",
"examination"=>{"name"=>"nnnnn", "shortname"=>"nnnnnn", "fee"=>"33",
"exam_statuses"=>{"first_application_date(3i)"=>"3", "first_application_date(2i)"=>"7", "first_application_date(1i)"=>"2014", "first_application_date(4i)"=>"16", "first_application_date(5i)"=>"43", "last_application_date(3i)"=>"5", "last_application_date(2i)"=>"7", "last_application_date(1i)"=>"2014", "last_application_date(4i)"=>"16", "last_application_date(5i)"=>"43", "examination_id"=>""}}}
Unpermitted parameters: exam_statuses

You have a few issues here.
1. Controller
In your new method you need to build exam_status for examination:
# GET /examinations/new
def new
#examination = Examination.new
#examination.build_exam_status
end
And in your examination_params method, you need to change it to:
def examination_params
params.require(:examination).permit(:name, :shortname, :fee, exam_status_attributes: [:first_application_date, :last_application_date, :_destroy])
end
Notice that :exam_status needs to be exam_status_attributes and it doesn't need examination_id param (Rails takes care of that). Also note that I added _destroy. This is a flag used to destroy the object.
2. Nested fields partial
Remove examination_id field from the partial.
<div class="nested-fields">
<%= f.input :first_application_date, as: :datetime %>
<%= f.input :last_application_date, as: :datetime %>
</div>
These changes will take care of the issue.

Related

Rails nested form attributes not getting saved

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]

Rails 4 Nested Form "Unpermitted Parameters" Not Answered Anywhere Else

My nested form is not working properly no matter what I try and I searched all the StackExchange's for a solution to this seemingly easy problem. This is where I am right now to get it to work at show up in the view at all.
The form is using the Event controller create action from a non-restful location, hence the global variable (a pages controller, with a specific page, where the form is generated). My ticket model gets generated when the nested form is submitted, and the Event ID gets passed, but it doesn't fill in the "Name" field for the ticket model because it says "Unpermitted Parameters: Ticket." But they're defined as whitelisted in the Events controller! Argh! I'm thinking something is wrong with the form, but nothing I try seems to work.
Any help would be appreciated.
* UPDATED CODE THAT IS NOW WORKING *
Form.html.erb:
<div class="form-inputs">
<%= simple_form_for #event, :html => { :class => 'form-horizontal' } do |f| %>
<div class="row">
<div class="col-xs-6">
<%= f.input :name, class: "control-label" %>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<%= f.simple_fields_for :tickets do |ticket| %>
<%= ticket.input :name %>
<% end %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
launchpad_path, :class => 'btn btn-default' %>
<% end %>
</div>
</div>
Event_Controller.rb
def new (this is totally skipped and unnecessary)
#event = Event.new
#ticket = #event.tickets.build
end
def create
#event = current_user.events.build(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Your event was created.' }
else
format.html { render :new }
end
end
end
def event_params
params.require(:event).permit(:name, tickets_attributes: [ :name, :id, :event_id, :_destroy ])
end
Pages_Controller.rb (where the form originate
def new
#event = Event.new
#ticket = #event.tickets.build
end
Event.rb
class Event < ActiveRecord::Base
# Database Relationships
has_many :tickets, dependent: :destroy
accepts_nested_attributes_for :tickets, :allow_destroy => true
end
Ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :event
end
Routes.rb
resources :events do
resources :tickets
end
As well as the information from Alejandro (which is correct), you also have f.simple_fields_for #ticket, ... whereas you should have f.simple_fields_for :tickets, ...
If you check your log/development.log for the Processing by EventsController#create the line after will be a Parameters: line, you'll see that the parameters that have been sent through are under a :ticket key instead of a :tickets_attributes key because of the fields_for error.
Fix that, and the permit line and you should be fine.
Update
Hopefully you realized that you also don't need the #ticket = #event.tickets.build(event_params[:ticket_attributes]) line at all once that fields_for is fixed too. The setting of all the associated tickets is all done via the Event object thanks to the accepts_nested_attributes_for helper.
Just, remove from create action this line:
#ticket = #event.tickets.build(event_params[:ticket_attributes])
And, change your event_params:
def event_params
params.require(:event).permit(:name, :main_event_image, tickets_attributes: [:id, :name, :cost, :event_id, :registration_id, :created_at])
end
Te field name must be: tickets_attributes: [ ... (tickets in plural). I think this do the trick.
Edit: I'm agree with #smathy, if no fix to f.simple_fields_for :tickets ... it can't work.
Your new method must look like this:
def new
#new_event = Event.new
#new_event.tickets.build
end
I'm a fan of standards, and I prefer use #event instead of #new_event as in your form (it's part of convention over configuration on rails)
I was stuck at the same problem like crazy and at the end I was able to fix it... Try placing the binding.pry in the first line of create method and print the event_params hash and check if you see ticket_attributes hash inside of it ... That's when it ll throw unpermitted parameter ... And I see event has_many tickets , so I am guessing ticket_attributes needs to be pluralized to be tickets_attributes

Using jQuery Tokeninput within a nested form partial

I'm using jQuery Tokeninput as shown in this Railscast. I'd like to combine this functionality in a nested form but get the error
undefined method `artists' for #<SimpleForm::FormBuilder:0x007febe0883988>
For some reason its not recognizing the track parameter in my form builder which is stopping me to get a hold of albums I have on record.
<div class="input">
<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.artists.map(&:attributes).to_json} %>
</div>
Keep in mind this works in my track form but just not in my album form since its nested. What should I do to get this to work?
class ArtistsController < ApplicationController
def index
#artists = Artist.order(:name)
respond_to do |format|
format.html
format.json {render json: #artists.tokens(params[:q])}
end
end
end
Models
class Artist < ActiveRecord::Base
has_many :album_ownerships
has_many :albums, through: :album_ownerships
has_many :featured_artists
has_many :tracks, through: :featured_artists
def self.tokens(query)
artists = where("name like ?", "%#{query}%")
if artists.empty?
[{id: "<<<#{query}>>>", name: "Add New Artist: \"#{query}\""}]
else
artists
end
end
def self.ids_from_tokens(tokens)
tokens.gsub!(/<<<(.+?)>>>/) {create!(name: $1).id}
tokens.split(',')
end
end
class Albums < ActiveRecord::Base
attr_reader :artist_tokens
accepts_nested_attributes_for :tracks, :reject_if => :all_blank, :allow_destroy => true
has_many :albums_ownerships
has_many :artists, through: :albums_ownerships
def artist_tokens=(ids)
self.artist_ids = Artist.ids_from_tokens(ids)
end
end
class Track < ActiveRecord::Base
attr_reader :artist_tokens
belongs_to :album
has_many :featured_artists
has_many :artists, through: :featured_artists
def artist_tokens=(ids)
self.artist_ids = Artist.ids_from_tokens(ids)
end
end
class AlbumOwnership < ActiveRecord::Base
belongs_to :artist
belongs_to :album
end
class FeaturedArtist < ActiveRecord::Base
belongs_to :artist
belongs_to :track
end
Album Form
<%= simple_form_for(#album) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<h1>Tracks</h1>
<%= f.simple_fields_for :tracks do |track| %>
<%= render 'track_fields', :f => track %>
<% end %>
<div id='links'>
<%= link_to_add_association 'Add Field', f, :tracks %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Track Partial
<div class="field">
<%= f.input :name %><br>
</div>
<div class="input">
<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.artists.map(&:attributes).to_json} %>
</div>
JS
$(function() {
$('#track_artist_tokens').tokenInput('/artists.json', {
prePopulate: $("#track_artist_tokens").data("pre"),
theme: 'facebook',
resultsLimit: 5
});
});
UPDATE
As mentioned by nathanvda, I needed to use f.object in order for the artists to be recognized. So in my partial I now have:
<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.object.artists.map(&:attributes).to_json, class: 'test_class'} %>
In my js I also needed to call the token input method before/after insertion:
$(function() {
$('.test_class').tokenInput('/artists.json', {
prePopulate: $(".test_class").data("pre"),
theme: 'facebook',
resultsLimit: 5
});
$('form').bind('cocoon:after-insert', function(e, inserted_item) {
inserted_item.find('.test_class').tokenInput('/artists.json', {
prePopulate: $(".test_class").data("pre"),
theme: 'facebook',
resultsLimit: 5
});
});
});
The only remaining issue I have is the the tracks_attributes not being saved. I ran into an issue similar to this in the past in this post but the two main difference is the second level of nesting involved and that I used a join table within my nested form. I'm not entirely sure if or how any of that code would translate over but I believe this is most likely problem. As far as the permitted params of my albums_controller here's what they looks like.
def album_params
params.require(:album).permit(:name, :artist_tokens, tracks_attributes: [:id, :name, :_destroy, :track_id])
end
If you need to acces the object of a form, you need to write f.object, so I think you should just write f.object.artists.
Your "data-pre" => f.artists... is calling the artists method on f which is the form builder and doesn't have an #artists method.
Try this instead:
In the album form, change the render partial line to this:
<%= render 'track_fields', :f => track, :artists => #artists %>
And then use this in the track partial:
<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => artists.map(&:attributes).to_json} %>
UPDATED
Let's back up a step. From your code it looks like you need to populate a data-pre attribute with the attributes of a collection of artists.
The problem is you're calling f.artists where f is the FormBuilder and doesn't know anything about artists. This is why you're getting undefined method 'artists'...
The solution is make a collection of artists available to the view and its partials. One way to do this:
class AlbumsController < ApplicationController
...
def new
#album = Album.new
#artists = Artist.order(:name) # or some other subset of artists
end
...
def edit
#album = Album.find params[:id]
#artists = Artist.order(:name) # or perhaps "#artists = #album.artists", or some other subset of artists
end
end
and then in new.html.erb and edit.html.erb, pass #artists to the form partial:
... # other view code
<%= render 'form', album: #album %>
... # other view code
and then in your form partial:
... # other view code
<%= f.simple_fields_for :tracks do |track_form| %>
<%= render 'track_fields', :f => track_form %>
<% end %>
... # other view code
finally, in your track partial:
... # other view code
<div class="input">
<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => #artists.map(&:attributes).to_json} %>
</div>
... # other view code
Does that make sense?

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, 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