I have read a number of posts about this and I'm still confused. I'm relatively new to Rails. I'm on Rails 3.2.8 and Ruby 1.9.3.
I have a form where I want to create a record for 2 different tables. It's a many-to-many relationship (logbooks can have multiple aircraft and aircraft will appear in multiple logbooks).
Here's my code:
# ----- Models ----- #
class Logbook < ActiveRecord::Base
has_and_belongs_to_many :aircrafts
accepts_nested_attributes_for :aircrafts
end
class Aircraft < ActiveRecord::Base
belongs_to :logbook
end
# ----- Logbooks Controller ----- #
def my_method
#logbook = Logbook.new
#aircraft = Aircraft.new
end
# ----- View ----- #
<%= form_for #logbook, validate: true, remote: true do |f| %>
<%= f.label :flight_date, "Flight Date" %>
<%= f.text_field :flight_date %>
...
<%= f.fields_for :aircrafts, validate: true, remote: true do |a| %>
<%= a.label :aircraft_id, "Aircraft ID" %>
<%= a.text_field :aircraft_id %>
...
<% end %>
<% end %>
The logbook fields are rendering fine, but the aircraft fields don't render.
Any ideas?
Try changing the controller to:
def my_method
#logbook = Logbook.new
#aircraft = #logbook.aircrafts.build
end
Because #aircraft need belongs_to a Logbook, so the nested_form will know how to build the form.
Note: if you will not use the variable #aircraft you don't need to declare it, just use #logbook.aircrafts.build on controller
If i recall correctly, the fields_for will generate based off the number of objects in the has many that exist for the parent. Since your #logbook doesn't have any aircrafts, no fields appear. Try changing the action:
def my_method
#logbook = Logbook.new
#logbook.aircrafts << Aircraft.new
end
Now, if you need to make something in the UI to add more than one logbook, you need to create an add button. You can just increment the amount of aircraft records you build based off that:
<%= form_tag new_logbook_path, :method => :get do %>
<%= hidden_field_tag "amount", #amount %>
<%= submit_tag "Add" %>
<% end %>
Then the action would be:
def my_method
#amount = params[:amount] || 1
#logbook = Logbook.new
#amount.times { #logbook.aircrafts << Aircraft.new }
end
Related
I'm trying to generate records according when somebody choose a date it will save more records according an input value.
Controller
##./app/controller/user_job_controller.rb
def new
#user_job = UserJob.new
end
def user_job_params
params.require(:user_job).permit!
end
Model
#user_job.rb
after_save :save_default_values, :if => :new_record?
private
def save_default_values
if params[:dateadd].to_i > 0
params[:dateadd].to_i.times do |i|
self.name = params[:name]
self.dateuser = params[:dateuser] + i+ 1.day
end
else
self.name = params[:name]
self.dateuser = params[:dateuser]
end
end
View
<%= form_with(model: user_job) do |form| %>
Name:<%= form.text_field :name %>
Date: <%= text_field_tag "dateuser", params[:dateuser].to_i %>
Date added: <%= text_field_tag "dateadd", params[:dateadd].to_i %>
<%= form.submit %>
<% end %>
Example with data:
#form values
Name: test
Date: 23-09-2021
days added: 2
Result Wanted:
#it will do on console or ruby on rails logs
insert into user_jobs (id:1, name:test, dateuser: 23-09-2021),
insert into user_jobs (id:2, name:test, dateuser: 24-09-2021),
insert into user_jobs (id:3, name:test, dateuser: 25-09-2021),
The log is showing that is not
Processing by UserJobsController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "user_job"=>{"name"=>"test"}, "dateuser"=>"2021-09-24", "dateadd"=>"2", "commit"=>"Create User job"}
Unpermitted parameter: :name
Result log:
insert into user_jobs(created_at: 23-09-2021)
Can you please help me to save the data correctly?
There are some problems in the codes.
1. params are only accessible in controller, you wanted to read it in your model
There are many ways to pass params value to model, for example, you can add attr_accessor :dateadd to the model.
But I think a clearer way is to extract the behavior in :save_default_values into a new public method and call it in the controller.
2. params[:name] is an attribute in the model, you should use strong parameters to assign the attributes
3. it's apparently that dateuser is a column of user_jobs, isn't it?
We can use date_field to pick the date and also add this field into strong parameter.
4. After a record saved, that record is not new record any more since it has id value.
Try to use after_create or after_save on: :create. However, I don't use this callback in the code below, but it's just personal choice :)
# user_job.rb
class UserJob < ApplicationModel
# add new public method
def add_additional_jobs!(days)
(1..days).each do |n|
UserJob.create!(name: name, dateuser: dateuser + n.days)
end
end
end
# user_jobs_controller.rb
class UserJobsController < ApplicationController
def new
#user_job = UserJob.new
end
def create
UserJob.transaction do
#user_job = UserJob.create!(user_job_params)
if params[:dateadd].to_i > 0
#user_job.add_additional_jobs!(params[:dateadd].to_i)
end
end
end
private
def user_job_params
params.require(:user_job).permit(:name, :dateuser)
end
end
Then in the view
<%= form_with(model: user_job) do |form| %>
Name:<%= form.text_field :name %>
Date: <%= form.date_field :dateuser %>
Date added: <%= text_field_tag "dateadd", params[:dateadd].to_i %>
<%= form.submit %>
<% end %>
This seems easy but I can't figure out how to do it
I'm trying to implement Ransack search in my rails app and for that I have a generated a model Cofounder which don't have any fields in it and I have association between Cofounder and CustomUser model which has email and other fields but i only want to search through email using ransack, i have this association between these two:
has_many :cofounders
belongs_to :CustomUser
(Do i need to have id if Yes how do i do it)
CoFounder Model
class Cofounder < ActiveRecord::Base
belongs_to :CustomUser
end
CofoundersController
class CofoundersController < ApplicationController
layout "custom_layout", only: [:index]
def index
#q = Cofounder.ransack(params[:q])
#cofounders = #q.result
#cofoundes = CustomUser.all
#countries = Country.all
end
end
and in my index view
<% search_form_for #q, url: cofounders_index_path do |f| %>
<%= f.search_field :email_cont, placeholder: "Search Here" %>
<%= f.submit "Search" %>
<% end %>
<%= select_tag "search_by_country", options_for_select(#countries.collect{|x| [x.name]} , params[:search_by_country] ),{prompt: "Select country"} %><br><br>
<%= select_tag "choose_a_role", options_for_select(#cofoundes.collect{|x| [x.first_name]} , params[:choose_a_role] ),{prompt: "Choose A Role", id: "select", multiple: true} %><br><br>
<% #cofoundes.each do |cofounder| %>
<%= cofounder.email %>
<%end%>
it is giving me error code
undefined method `email_cont' for #
i know email_cont if not a field in cofounder and i'm accessing that field in cofounder which is giving me error, how do i get those values in cofounder for successful Ransack search.
any help would be appreciated :)
I have a Category model that has_many Skills.
I have a page where I want a user to be able to add a single new skill to a selected category via a textbox (just the name of the skill), while listing all existing on a page categories and the skills within those categories.
I can currently display all the categories with their contained skills, but I'm not sure how to make it so a user can only create a new skill without being able to edit all the other skills that belongs_to a category.
Here is the code that I have currently:
Controller:
def show
. . .
#needed for showing all categories
#categories = current_user.categories
#needed to show a blank skill when editing a category
#skills = []
#categories.each do |cat|
#skills << cat.skills.build
end
. . .
end
View:
<% #categories.each do |category| %> # I skip most of the code related to formatting
<%= simple_form_for(category) do |f| %>
<%= f.fields_for :skills do |s| %>
<div class="inputs">
<%= s.input :name %>
<%= s.hidden_field :category_id %>
<%= s.hidden_field :id %>
</div>
<% end %>
<%= f.button :submit %>
<% end %>
<% end %>
And it ultimately results in a form that looks like this:
I have tried to change f.fields_for :skills to f.fields_for category.skills.last(because the last object in category.skills is a new skill that was created in the controller) and this results in a blank text box that looks like what I need:
But when I submit the form, I get a "param not found: category" error (that points to my params.require(:category). . . line in my Category controller.
I'm not sure why the form works if I use the symbol for :skills but doesn't work when I use category.skills.last (or even category.skills.each - I get a different error when I do that: undefined method `model_name' for Enumerator:Class).
What am I doing wrong?
TRY THIS
f.fields_for :skills, category.skills.last
:skills represents relation name. (category has many skills)
category.skills.last represents the object that you want to use in form.
e.g
f.fields_for :skills, category.skills.build do |s| .. end
or
f.fields_for :skills, category.skills.last(3) do |s| ... end
Background: Users and communities share a
has_many :through
relationship. Each community has a "community_type" string that identifies it (ie "Gender", "City", etc.).
Objective: In my edit form, I'd like to allow the user to edit his :community_ids based on community type. Something like:
<%= form_for current_user do |f| %>
<%= f.collection_select(:community_ids, Community.filtered_by("Gender"), :id, :name) %>
<%= f.collection_select(:community_ids, Community.filtered_by("City"), :id, :name) %>
<% end %>
The issue is that the form only accepts the last form field value for :community_ids - in this case being the "City" - rather than merging all of them as one big :community_ids array.
Solution:
For those interested, I ended up refactoring my model code from the answers below to this:
%W[ community1 community2 community3 community4 ].each do |name|
define_method "#{name}" do
self.communities.filtered_by("#{name}").map(&:name)
end
define_method "#{name}_ids" do
self.communities.filtered_by("#{name}").map(&:id)
end
define_method "#{name}_ids=" do |val|
self.community_ids += val
end
end
Is there a reason you're using select boxes for a has_many relationship? It seems checkboxes would be more appropriate. If you want to go with select boxes, I don't think you can use FormHelper#select, because as far as I know, it's expecting a single value, and your community_ids is an array. This is why it's only picking one of the values you give it.
For a select box (or any field), you can combine the values across fields by adding [] to the parameter name which tells Rails that the parameter is an array of values. You can do this by using regular select_tag to create the fields, and setting the parameter name as follows:
<%= form_for current_user do |f| %>
<%= select_tag("user[community_ids][]", options_for_select(Community.filtered_by("Gender").map{|c| [c.name, c.id]}, :selected => current_user.communities.filtered_by("Gender").first.id) %>
<%= select_tag("user[community_ids][]", options_for_select(Community.filtered_by("City").map{|c| [c.name, c.id]}, :selected => current_user.communities.filtered_by("City").first.id) %>
<% end %>
You could also go with Ryan's approach of sending separate parameters, though one downside is your User model will have to be very aware of the types of communities that exist, and you'll have to write separate logic in the User model for each type of community. This will make your resources less modular. But if you do go that way, I'd suggest using pseudo-attributes instead of a before_save so that your community_ids get updated automatically from the params:
class User < ActiveRecord::Base
...
def community_gender_ids=(cg_ids)
self.community_ids ||= []
self.community_ids += cg_ids
end
def community_city_ids=(cc_ids)
self.community_ids ||= []
self.community_ids += cc_ids
end
...
end
And then your select_tag calls would look something like this:
<%= form_for current_user do |f| %>
<%= select_tag("user[community_gender_ids][]", options_for_select(Community.filtered_by("Gender").map{|c| [c.name, c.id]}, :selected => current_user.communities.filtered_by("Gender").first.id) %>
<%= select_tag("user[community_city_ids][]", options_for_select(Community.filtered_by("City").map{|c| [c.name, c.id]}, :selected => current_user.communities.filtered_by("City").first.id) %>
<% end %>
Updated to complete tsherif's (better than my original) answer.
view.rb
<%= form_for current_user do |f| %>
<%= f.collection_select(:community_gender_ids, Community.filtered_by("Gender"), :id, :name, {}, id: 'community-gender-options') %>
<%= f.collection_select(:community_city_ids, Community.filtered_by("City"), :id, :name, {}, id: 'community-city-options') %>
<% end %>
model.rb
def community_gender_ids=(cg_ids)
self.community_ids ||= []
self.community_ids += cg_ids
end
def community_city_ids=(cc_ids)
self.community_ids ||= []
self.community_ids += cc_ids
end
def community_gender_ids
self.communities.select(:id).where(:community_type => 'gender').map(&:id)
end
def community_city_ids
self.communities.select(:id).where(:community_type => 'city').map(&:id)
end
Alternatively, you could write some CoffeeScript/Javascript to bind to the select tags and add the IDs to a hidden value which is then submitted to the server with the form.
I'm using Rails 2.3.2, and trying to get a nested object form to work properly. I've narrowed my problem to the issue that Rails is not setting my nested form elements with the *_attributes required to initiate the accepts_nested_attributes_for processing.
My model code is:
class Person < Party
has_one :name, :class_name => "PersonName"
accepts_nested_attributes_for :name, :allow_destroy => true
end
class PersonName < ActiveRecord::Base
belongs_to :person
end
My view code looks like this (I'm using HAML):
%h3 New customer
= error_messages_for :person, :person_name, :name, :country
- form_for :person, :url => collection_url, :html => {:class => 'MainForm'} do |person_form|
- #person.build_name unless #person.name
- person_form.fields_for :name do |name_form|
= name_form.label :given_name, "First Name:"
= name_form.text_field :given_name
= name_form.label :family_name, "Last Name:"
= name_form.text_field :family_name
= hidden_field_tag :inviter_id, params[:inviter_id]
= hidden_field_tag :inviter_code, params[:inviter_code]
%p= submit_tag "Create"
= link_to 'Back', collection_url
Instead of params being:
{"person"=>{"name_attributes"=>{"given_name"=>"Fred", "family_name"=>"Flintstone"}}, ...}
I get:
{"person"=>{"name"=>{"given_name"=>"Fred", "family_name"=>"Flintstone"}}, ...}
As a result, I get a TypeMismatch exception. I've followed the documentation from Ryan Daigle. I've also followed the advice from this blog and the complex-forms-example.
Using Firebug, I went through my form and adjusted the name attribute of the input tags from name to name_attributes. This produced the params with name_attributes, and the create worked fine.
I'm stuck as I cannot figure out why my form is not producing the *_attributes form of the name.
Another thing I tried is I got the complex_form_example working in my environment. I've gone through every inch of the controller, models and views and compared it to my code. I cannot find what is different. I know this is something small, and would appreciate any help!
Thanks!
Post backs do not get routed to the right place
def new
#person = Person.new
end
<% form_for #person do |f| %>
<% f.fields_for :name_attributes do |p| %>
...
<% end %>
<% end %>
Post backs get routed to the right place
def new
#person = Person.new
#person.name = PersonName.new # << this allows fields_for :relation vs :relation_attributes
end
<% form_for #person do |f| %>
<% f.fields_for :name do |p| %>
...
<% end %>
<% end %>
No need to #person.name again in #create
Try to use an actual object for form_for:
form_for :person => form_for #person
I have just been struggling for about an hour with exactly the same problem!
Follow nowk's pattern for the new method in the controller, then put this in your view
<% form.fields_for :name, #person.name do |name_form| %>
<% end %>
Good luck if you try it, that's what worked for me.
Not sure why this isn't working for, but as a workaround, you could just use params[:name] in your controller#create method to update the person record.
person = Person.new(params[:person])
person.name << PersonName.new(params[:name])
Unfortunately, I still have not been able to figure out why this form wouldn't work with nested object forms. I stripped it down to the simplest data, started over using the complex-form-example as a start. I ended using the active_presenter to gather x-object data from the form. I'll revisit nested object forms sometime in the form. Thanks for your help.
Thought I would share my solution as it's slightly different - in my case I want the nested attributes to be dynamic.
In new action:
case params[:type]
when "clubber"
#account = resource.send "build_#{params[:type]}"
when "promoter"
#account = resource.send "build_#{params[:type]}"
when "company"
#account = resource.send "build_#{params[:type]}"
when "venue_owner"
flash[:notice] = 'We ask that venue owners register via the web. Thanks.'
redirect_to root_path and return
end
In my view:
= f.fields_for #account.class.name.downcase+'_attributes' do |form|
Pretty ghetto, but it works.