I have the following models:
class FieldEntryValue < ActiveRecord::Base
belongs_to :field_entry
end
and
class FieldEntry < ActiveRecord::Base
belongs_to :field
belongs_to :media
has_many :field_entry_values, :dependent => :destroy
accepts_nested_attributes_for :field_entry_values, :allow_destroy => true
end
The FieldEntriesController has the following methods:
def new
#field_entry = FieldEntry.new
session[:media_id] = params[:media_id]
session[:field_id] = params[:field_id]
#field_entry.field = Field.find(params[:field_id])
generate_fields(true)
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #field_entry }
end
end
def create
#field_entry = FieldEntry.new(params[:field_entry])
#field_entry.media_id = session[:media_id]
#field_entry.field_id = session[:field_id]
generate_fields(false)
respond_to do |format|
if #field_entry.save
flash[:notice] = 'FieldEntry was successfully created.'
format.html { redirect_to(#field_entry) }
format.xml { render :xml => #field_entry, :status => :created, :locati$
else
format.html { render :action => "new" }
format.xml { render :xml => #field_entry.errors, :status => :unprocess$
end
end
end
def generate_fields(do_build)
#ftype = #field_entry.field.field_type.name
if #ftype == 'string'
#field_entry.field_entry_values.build if do_build
#field_entry.field_entry_values[0].key='name'
elsif #ftype == 'url'
2.times{ #field_entry.field_entry_values.build } if do_build
#field_entry.field_entry_values[0].key='name'
#field_entry.field_entry_values[1].key='url'
elsif #ftype == 'imdb'
1.times{ #field_entry.field_entry_values.build } if do_build
#field_entry.field_entry_values[0].key='value'
end
end
And I have a partial that is loaded by new and edit:
<% form_for(#field_entry) do |f| %>
<%= f.error_messages %>
<% f.fields_for :field_entry_values do |fv_f| %>
<p>
<%= fv_f.label :key %><br />
<%= fv_f.text_field :value %>
</p>
<% end %>
<p>
<%= f.submit 'Save' %>
</p>
<% end %>
The view should show a variable count fields depending on the type and with a given attribute key as label and value for the data.
For the type string as an example I need a label "Name" (that should be the key) and value
This works mostly except "fv_f.label :key" shows the string key and not the value of the key-field.
How can I show the value of #field_entry.field_entry_values.key as label in the view?
How can I make the Controller better/shorter. As an example, do I really need to set #field_entry.field_entry_values[0].key='name' in the new and the create method to to get it displayed in the view and saved in the database?
thanx for suggestions ;) I am a Rails newby I hope I did it not too wrong ;)
try this for 1.
<% form_for(#field_entry) do |f| %>
<%= f.error_messages %>
<% #field_entry.field_entry_values.each do |fv| %>
<% f.fields_for fv do |fv_f| %>
<p>
<%= fv_f.label :value, fv.key %><br />
<%= fv_f.text_field :value %>
<%= # this will eliminate the need for generate_fields in create %>
<%= fv_f.hidden_field :key %>
</p>
<% end %>
<% end %>
<p>
<%= f.submit 'Save' %>
</p>
<% end %>
I found a better solution for my Problem 1, in rails 2.3 there is a object attribute for the fields_for loop target to get the object of the field:
<% form_for(#field_entry) do |f| %>
<%= f.error_messages %>
<% f.fields_for :field_entry_values do |fv_f| %>
<p>
<%= fv_f.label :value, fv_f.object.key%>
<br />
<%= fv_f.text_field :value %>
<%= fv_f.hidden_field :key %>
</p>
<% end %>
<p>
<%= submit_tag %>
</p>
<% end %>
Point 2 can not be improved much, maybe the generate_fields method could be loaded as a filter. But other than that, I donĀ“t see anything at the moment, at least with this database structure.
But thanks for the help: so1o, at least the thing with hidden_field was helpfully.
Related
I tried using pluck as a shortcut to select one or more attributes without loading a bunch of records just to grab the attributes I want. Which is pretty much 1:1 the intended use (see here: Pluck).
There are 2 tables: "demands" and "parents".
the idea is to create a new entry in the table "demands". The table "demands" contains a column "parent_id" and the demand/parent models are properly associated through belongs_to and has_many.
this is my code using the pluck method:
<%= form.label :parent_id %><br>
<% if can? :optimize, Matching %> <----TEST FOR ADMIN RIGHTS
<%= form.collection_select :parent_id, Parent.all, :id, :fullname %>
<% else %>
<%= #parent.pluck(:name) %> <-----ERROR ERROR ERROR
<%= form.hidden_field :parent_id, value: #parent.pluck(:id) %>
<% end %><br><br>
The corresponding datatable "parents" obviously contains all three of these values, firstname, name and id.
However, this is my error:
undefined method `pluck' for nil:NilClass
error occurs in /app/views/demands/_form.html.erb
update: this is the complete _form.html.erb
<%= form_for Demand.new do |form| %>
<%= form.label :parent_id %><br>
<% if can? :optimize, Matching %>
<%= form.collection_select :parent_id, Parent.all, :id, :fullname %>
<% else %>
<%= #parent.pluck(:name) %>
<%= form.hidden_field :parent_id, value: #parent.pluck(:id) %>
<% end %><br><br>
<%= form.label :demand %><br>
<%= form.number_field :demand %>
<%= form.label :shift_id %><br>
<%= form.collection_select :shift_id, Shift.all, :id, :description %>
<div class="actions">
<%= form.submit 'Post a new job', {:class => "btn btn-primary"} %>
</div>
<% end %>
This is the demand controller action "create"
def new
#demands = Demand.new
if cannot? :optimize, Matching
#parent = Parent.where(parents: {firstname: current_user.firstname, name: current_user.name})
end
end
def create
#demand = Demand.new(demand_params)
respond_to do |format|
if #demand.save
format.html { redirect_to #demand, notice: 'Demand was successfully created.' }
format.json { render :show, status: :created, location: #demand }
else
format.html { render :new }
format.json { render json: #demand.errors, status: :unprocessable_entity }
end
end
end
Thanks in advance for any advice!
Your #parent instance var is only defined in the create action. So, if #demand.save returns false (Invalid object), the exception occurs because #parent is nil
I have a nested form as detailed below with a checkbox which I'm hoping to use to note only the records I want to update. I've checked the html and I'm registering the correct index for each record with it. The html for both an example attribute and the checkbox are as follows:
<input id="game_game_rosters_attributes_0_game_id" name="game[game_rosters_attributes][0][game_id]" type="hidden" value="127">
<input id="add_0" name="add[0]" type="checkbox" value="false">
Now, I think I'm supposed to figure out which game_roster_attributes I want to update by checking to see if the checkbox with the same index has a value of "true". But I'm unsure how to do this in my games_controller.rb, because it's currently set to mass assignment of my attr_accessible variables with #game.update_attributes()...
games_controller.rb
def update
#game = Game.find(params[:id])
respond_to do |format|
if #game.update_attributes(params[:game])
format.html { redirect_to #game, notice: 'Game was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit"}
format.json { render json: #game.errors, status: :unprocessable_entry }
end
end
end
Nested Form
<%= form_for #game do |f| %>
# #game fields for editing here...
<% roster_options.each.with_index do |option, index| %>
<%= f.fields_for :game_rosters, option do |fields| %>
<%= fields.object.player.full_name %>
<%= fields.hidden_field :player_id %>
<%= fields.hidden_field :game_id %>
<%= check_box_tag 'add[' + index.to_s + ']', false %>
<% end %>
<% end %>
<% end %>
Models
game.rb
class Game < ActiveRecord::Base
attr_accessible :date, :game_rosters_attributes
has_many :game_rosters
has_many :players, :through => :game_rosters
accepts_nested_attributes_for :game_rosters, allow_destroy: true
end
game_roster.rb
class GameRoster < ActiveRecord::Base
attr_accessible :active, :winner, :player_id, :game_id, :placement
belongs_to :game
belongs_to :player
end
game_controller.rb
def new
#game = Game.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #game }
end
end
def edit
#game = Game.find(params[:id])
#game_roster = GameRoster.joins(:player).where(game_id: params[:id]).order('winner DESC, placement DESC')
#options = SeasonRoster.where("season_id = (?) AND NOT EXISTS ( SELECT * FROM game_rosters INNER JOIN games ON games.id = game_rosters.game_id WHERE game_rosters.player_id = season_rosters.player_id AND games.id = (?))", #game.season_id.to_s, #game.id.to_s)
#roster_options = Array.new
#options.each do |o|
c = GameRoster.new
c.player_id = o.player_id
c.challenge_id = params[:id]
c.winner = false
#roster_options.push(c)
end
end
I am not sure about your requirement. I assume that you want to save only those rosters with their 'add' field checked and remove the rest of them.
roster.rb
class Roster < ActiveRecord::Base
attr_accessor :add
end
game.rb
class Game < ActiveRecord::Base
before_save :mark_rosters_for_removal
def mark_rosters_for_removal
rosters.each do |roster|
unless roster.add
roster.mark_for_destruction
end
end
end
end
Nested Form
<%= form_for #game do |f| %>
# #game fields for editing here...
<% roster_options.each.with_index do |option, index| %>
<%= f.fields_for :game_rosters, option do |fields| %>
<%= fields.object.player.full_name %>
<%= fields.hidden_field :player_id %>
<%= fields.hidden_field :game_id %>
<%= fields.check_box :add, false %>
<% end %>
<% end %>
<% end %>
I would use a before_save and use the params hash to check to see if the check-box is checked or not. Then use the .delete method to delete that part of the params hash. Then #game.update_attributes() would only update the rosters that are still in the hash (whom's check-boxes are checked).
Nested Form
<%= form_for #game do |f| %>
# #game fields for editing here...
<% roster_options.each.with_index do |option, index| %>
<%= f.fields_for :game_rosters, option do |fields| %>
<%= fields.object.player.full_name %>
<%= fields.hidden_field :player_id %>
<%= fields.hidden_field :game_id %>
<%= check_box_tag :add, false %> #Notice change!!!!!
<% end %>
<% end %>
<% end %>
game.rb
class Game < ActiveRecord::Base
before_save :check_for_deletion
...
def check_for_deletion
index = 0
roster = params[:game][:game_rosters_attributes]
while !roster["#{index.to_s}"].nil?
if roster["#{index.to_s}"][:add] == "true" # Or "checked" or whatever. I forget the actual value
roster.delete "#{index.to_s}"
end
index += 1
end
end
end
You might have to play around with this a bit depending on how your params hash looks like. I did my best to figure it out. I'm also not 100% that "#{index.to_s}" will work (or it could even be overkill). But this should give you a good head start.
It seems like you are adding all players to a game and then deleting them on update if "add" is not checked.
If this is the case, then you can add a checkbox for _destroy and it will be handled for you, as you have added the "allow_destroy: true" flag on game_rosters in game.rb.
Note that it will now be a "remove" checkbox rather than an "add" checkbox.
Nested Form:
<%= form_for #game do |f| %>
# #game fields for editing here...
<% roster_options.each.with_index do |option, index| %>
<%= f.fields_for :game_rosters, option do |fields| %>
<%= fields.object.player.full_name %>
<%= fields.hidden_field :player_id %>
<%= fields.hidden_field :game_id %>
<%= fields.check_box :_destroy, false %>
<% end %>
<% end %>
<% end %>
From the Rails guide on nested forms:
"Don't forget to update the whitelisted params in your controller to also include the _destroy field"
http://guides.rubyonrails.org/form_helpers.html#nested-forms
So after taking some time away from the problem and coming back to it I found a simple enough solution. I ended up just reversing the :_destroy checkbox that I had been using in the mean time.
<%= fields.check_box :_destroy, {}, '0', '1' %>
Basically, I just reversed the value of what the check box would usually represent. You can see where I edited the nested form to use this method below.
Nested Form
<%= form_for #game do |f| %>
# #game fields for editing here...
<% roster_options.each.with_index do |option, index| %>
<%= f.fields_for :game_rosters, option do |fields| %>
<%= fields.object.player.full_name %>
<%= fields.hidden_field :player_id %>
<%= fields.hidden_field :game_id %>
<%= fields.check_box :_destroy, {}, '0', '1' %>
<% end %>
<% end %>
<% end %>
I have a admin section and inside the admin i have, Finalist and Images.
Im using, ruby 1.8.7-p249 and Rails 3.1.2
# model image.rb
class Admin::Image < ActiveRecord::Base
belongs_to :finalist
...
end
# model finalist.rb
class Admin::Image < ActiveRecord::Base
has_many :images
...
end
My ImagesController:
def new
#admin_finalist = Admin::Finalist.find(params[:finalist_id])
#admin_image = #admin_finalist.images.build
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #admin_image }
end
end
def edit
#admin_finalist = Admin::Finalist.find(params[:finalist_id])
#admin_image = #admin_finalist.images.find(params[:id])
end
def create
#admin_finalist = Admin::Finalist.find(params[:finalist_id])
#admin_image = #admin_finalist.images.new(params[:admin_image])
respond_to do |format|
if #admin_image.save
format.html { redirect_to admin_finalist_images_path(#admin_finalist) }
format.json { render :json => #admin_image, :status => :created, :location => #admin_image }
else
format.html { render :action => "new" }
format.json { render :json => #admin_image.errors, :status => :unprocessable_entity }
end
end
end
def update
#admin_finalist = Admin::Finalist.find(params[:finalist_id])
#admin_image = #admin_finalist.images.find(params[:id])
respond_to do |format|
if #admin_image.update_attributes(params[:admin_image])
format.html { redirect_to admin_finalist_images_path(#admin_finalist) }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #admin_image.errors, :status => :unprocessable_entity }
end
end
end
And my Images _form.html.erb:
<%= form_for([#admin_finalist, #admin_image]) do |f| %>
<% if #admin_image.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#admin_image.errors.count, "error") %> prohibited this admin_image from being saved:</h2>
<ul>
<% #admin_image.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :file %>
<%= f.file_field :file %>
</div>
<%- unless #admin_image.new_record? || !#admin_image.file? -%>
<div class="field">
<%= f.label :remove_file, "Remover?" %>
<%= f.check_box :remove_file %>
</div>
<%- end -%>
<div class="field">
<%= f.label :url %>
<%= f.text_field :url, :placeholder => "http://" %>
</div>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="actions">
<%= image_submit_tag("button/save.png") %>
<%= link_to image_tag("button/back.png"), admin_finalist_images_path %>
</div>
<% end %>
Everything seems perfect, but im getting this error when im trying to add an image to finalist at admin/finalists/1/images/new.
Showing .../app/views/admin/images/_form.html.erb where line #5 raised:
undefined method `admin_finalist_admin_images_path' for #<#<Class:0x102da0068>:0x102d9a870>
So, i tryed to tell rails to use a particular url:
<%= form_for([#admin_finalist, #admin_image], :url => admin_finalist_image_path(#admin_finalist.id, #admin_image)) do |f| %>
Saddly, this work only for editing. When i try to edit i get this error message:
No route matches {:controller=>"admin/images", :finalist_id=>1, :id=>nil, :action=>"show"}
And if i wanna create i need to do:
<%= form_for([#admin_finalist, #admin_image], :url => admin_finalist_images_path) do |f| %>
And if i try to edit i get this message:
No route matches [PUT] "/admin/finalists/1/images"
I dont know what to do... I tried everything without success, if anyone can help me would be much appreciated.
EDIT
The problem was that my models was namespaced. So, i rewrite all the app to fix this, and thats running normally.
[]`s
I'm struggling to come up with the proper way to design a form that will allow me to input data for two different models. The form is for an 'Incident', which has the following relationships:
belongs_to :customer
belongs_to :user
has_one :incident_status
has_many :incident_notes
accepts_nested_attributes_for :incident_notes, :allow_destroy => false
So an incident is assigned to a 'Customer' and a 'User', and the user is able to add 'Notes' to the incident. I'm having trouble with the notes part of the form. Here how the form is being submitted:
{"commit"=>"Create",
"authenticity_token"=>"ECH5Ziv7JAuzs53kt5m/njT9w39UJhfJEs2x0Ms2NA0=",
"customer_id"=>"4",
"incident"=>{"title"=>"Something bad",
"incident_status_id"=>"2",
"user_id"=>"2",
"other_id"=>"AAA01-042310-001",
"incident_note"=>{"note"=>"This is a note"}}}
It appears to be attempting to add the incident_note as a field under 'Incident', rather than creating a new entry in the incident_note table with an incident_id foreign key linking back to the incident.
Here is the 'IncidentNote' model:
belongs_to :incident
belongs_to :user
Here is the form for 'Incident':
<% form_for([#customer,#incident]) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :other_id, "ID" %><br />
<%= f.text_field :capc_id %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= label_tag 'user', 'Assign to user?' %>
<%= f.select :user_id, #users.collect {|u| [u.name, u.id]} %>
</p>
<p>
<%= f.label :incident_status, 'Status?' %>
<%= f.select :incident_status_id, #statuses.collect {|s| [s.name, s.id]} %>
</p>
<p>
<% f.fields_for :incident_note do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
And finally, here are the incident_controller entries for New and Create.
New:
def new
#customer = current_user.customer
#incident = Incident.new
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident_note = IncidentNote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #incident }
end
end
Create:
def create
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident = Incident.new(params[:incident])
#incident.customer = #customer
#incident_note = #incident.incident_note.build(params[:incident_note])
#incident_note.user = current_user
respond_to do |format|
if #incident.save
flash[:notice] = 'Incident was successfully created.'
format.html { redirect_to(#incident) }
format.xml { render :xml => #incident, :status => :created, :location => #incident }
else
format.html { render :action => "new" }
format.xml { render :xml => #incident.errors, :status => :unprocessable_entity }
end
end
end
I'm not really sure where to look at this point. I'm sure it's just a limitation of my current Rails skill (I don't know much). So if anyone can point me in the right direction I would be very appreciative. Please let me know if more information is needed!
Thanks!
Check api for fields_for method and scroll to one-to-many section.
Your model has many :incident_notes, not one incident_note, that is why it doesn't understand relationship and tries to find a field with this name.
So it should be:
<% f.fields_for :incident_notes do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
It iterates through all incident_notes assigned to incident and produces fields for each of them.
You also have to build at least one note in you new action, otherwise there will be none:
def new
#incident = Incident.new
#incident.incident_notes.build
# ...
end
I'm still super new with Rails, and just trying to get my first has_many through association set up.
Recipes have many ingredients, and each ingredient has an amount needed for the recipe. The ingredient_amount table has a recipe_id, an ingredient_id, and an amount.
When creating a new recipe, I want to be able to create these recipe/ingredient associations in the same place. In the end, I'm going to build an AJAX autocompleter for the ingredients. For now, as a baby step, I'd like to just assume the ingredient exists, and take care of checking once I've got this part down.
So, how can I make new.html.erb for recipes do this? How can I extend the form for more than one ingredient?
As it stands now, after going through http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
I still can't get any fields to add ingredients. The current code is below.
class Recipe < ActiveRecord::Base
has_many :ingredient_amounts
has_many :ingredients, :through => :ingredient_amounts
accepts_nested_attributes_for :ingredient_amounts, :allow_destroy => true
end
class IngredientAmount < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
end
class Ingredient < ActiveRecord::Base
has_many :ingredient_amounts
has_many :recipes :through => :ingredient_amounts
end
Here's new.html.erb as I have it currently:
<h1>New recipe</h1>
<% form_for #recipe do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :instructions %><br />
<%= f.text_area :instructions %>
</p>
<p>
<%= f.label :numberOfServings %><br />
<%= f.text_field :numberOfServings %>
</p>
<p>
<%= f.label :prepTime %><br />
<%= f.text_field :prepTime %>
</p>
<p>
<% f.fields_for :ingredient_amounts do |ingredient_form| %>
<%= ingredient_form.label :ingredient_formedient_id, 'Ingredient' %>
<%= ingredient_form.collection_select :ingredient_id, Ingredient.all, :id, :name, :prompt => "Select an Ingredient"%>
<%= ingredient_form.text_field :amount %>
<% unless ingredient_form.object.new_record? %>
<%= ingredient_form.label :_delete, 'Remove:' %>
<%= ingredient_form.check_box :_delete %>
<% end %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', recipes_path %>
The important bits of the recipe controller:
def new
#recipe = Recipe.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #recipe }
end
end
def create
#recipe = Recipe.new(params[:recipe])
respond_to do |format|
if #recipe.save
flash[:notice] = 'Recipe was successfully created.'
format.html { redirect_to(#recipe) }
format.xml { render :xml => #recipe, :status => :created, :location => #recipe }
else
format.html { render :action => "new" }
format.xml { render :xml => #recipe.errors, :status => :unprocessable_entity }
end
end
end
And... I have no idea where to start in the ingredient_amounts controller.
This was my very first stab, and I'm pretty sure it's not so close :)
def new
#recipe = Recipe.find(params[:recipe_id])
#ingredient = Ingredient.find(params[:ingredient_id])
#ingredient_amount = Recipe.ingredient_amounts.build
end
Thanks for the help!
I believe what you are looking for is 'Nested Model Forms'.
Try this link: http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
It's hard to know what to search for when you dont really know the terminology to begin with :)