Trying to insert data for models that has polymorphic associations - ruby-on-rails

I'm having a problem in trying to insert data into the db which has complex form involving two models and I searched every post on Stackoverflow to find the best possible answer, but I couldn't get anywhere near close in getting it right.
In my Models
class ClientIndividual < ActiveRecord::Base
has_one :client_assignment, :as => :clientassignmentable
accepts_nested_attributes_for :client_assignment
end
class ClientAssignment < ActiveRecord::Base
belongs_to :clientassignmentable, :polymorphic => true
end
In my ClientIndividual Controller
class ClientIndividualsController < ApplicationController
before_action :set_client_individual, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
def new
#client_individual = ClientIndividual.new
end
def create
#client_individual = ClientIndividual.new(client_individual_params)
respond_to do |format|
if #client_individual.save
format.html { redirect_to #client_individual, notice: 'Client individual was successfully created.' }
format.json { render action: 'show', status: :created, location: #client_individual }
else
format.html { render action: 'new' }
format.json { render json: #client_individual.errors, status: :unprocessable_entity }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client_individual
#client_individual = ClientIndividual.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def client_individual_params
params.require(:client_individual).permit(:title, :first_name, :middle_name,
:last_name, :date_of_birth, :work_phone, :home_phone, :mobile_phone, :email_address,
:preferred_contact_type, :residential_address, :residential_suburb, :residential_state,
:residential_postcode, :same_as_residential, :postal_address, :postal_suburb,
:postal_state, :postal_postcode, :emergency_contact_person, :emergency_phone_no,
:industry_type, :has_referral, :notes, clientassignmentable_attributes:
[:assignment_types_id, :employees_id, :start_date, :manager_id, :client_type] )
end
end
In my Client_Individual#new view
<%= form_for(#client_individual) do |f| %>
<div class="add_data_section">
<%= f.fields_for (:clientassignmentable) do |client_assignment_form| %>
........
........
<div class="row_container">
<div class="field 1" style="width: 409px;">
<%=client_assignment_form.label :assignment_types_id, "Assignment Type*"%>
</div>
<div class="dropdown">
<label>
<%= select('client_individual[clientassignmentable]', 'assignment_types_id', AssignmentType.all.collect {|assignment_type| [assignment_type.name, assignment_type.id ] }, {prompt: 'Select Assignment Type'}, :style =>"width: 264px;")%>
</label>
</div>
</div>
<div class="row_container">
<div class="field " style="width: 409px;">
<%=client_assignment_form.label :start_date, "Assignment Start Date"%>
</div>
<div class="inputfield">
<%=client_assignment_form.text_field :start_date, :class=>'assignmentdatepicker', :style=>'width: 260px;'%>
</div>
</div>
<div class="row_container">
<div class="field " style="width: 409px;">
<%=client_assignment_form.label :manager_id, "Manager Appointed*"%>
</div>
<div class="dropdown">
<label>
<%= select('client_individual[clientassignmentable]', 'manager_id', Employee.all.select{ |manager| manager.staff_rank_id == 2}.collect{|employee| [employee.first_name + " " + employee.last_name, employee.id ] }, {prompt: 'Select ManagerList'}, :style =>"width: 264px;")%>
</label>
</div>
</div>
<div class="row_container">
<div class="field 1" style="width: 409px;">
<%=client_assignment_form.label :employees_id, "Staff Appointed*"%>
</div>
<div class="dropdown">
<label>
<%= select('client_individual[clientassignmentable', 'employees_id', Employee.all.select{ |employee| employee.user_id == current_user_id}.collect{|employee| [employee.first_name + " " + employee.last_name, employee.id ] }, {prompt: 'Select CurrentUser'}, :style =>"width: 264px;")%>
</label>
</div>
</div>
<%=client_assignment_form.hidden_field :client_type%>
<% end %>
......
<% end %>
So what I have is that I got a ClientIndividual Model that has one ClientAssignment Model. So the ClientAssignment Model has ClientIndividual FK thus upon creating a new ClientIndividual data, I would expect a new ClientAssignment data that gets generated on the form. Thus in my controller, I declare my strong params like so
clientassignmentable_attributes: [:assignment_types_id, :employees_id, :start_date, :manager_id, :client_type]
But I click to save the new data, nothing's inserted into the db...
Then I check my development.log and it said the following.
Started POST "/client_individuals" for 127.0.0.1 at 2014-03-30 19:58:52 +1100
Processing by ClientIndividualsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0Z+BJcjQE7oa+uzdo/2sKRPiV01EQDXfedMkLE7pTjg=", "groups"=>{"id"=>"1"}, "client_individual"=>{"clientassignmentable"=>{"assignment_types_id"=>"1", "start_date"=>"03/31/2014", "manager_id"=>"13", "employees_id"=>"25", "client_type"=>""}, "title"=>"t", "first_name"=>"t", "middle_name"=>"t", "last_name"=>"t", "date_of_birth"=>"", "work_phone"=>"", "home_phone"=>"", "mobile_phone"=>"", "email_address"=>"admin#example.com", "residential_address"=>"t", "residential_suburb"=>"t", "residential_state"=>"t", "residential_postcode"=>"t", "same_as_residential"=>"0", "postal_address"=>"t", "postal_suburb"=>"t", "postal_postcode"=>"t", "emergency_contact_person"=>"", "emergency_phone_no"=>"22223", "industry_type"=>"", "notes"=>""}, "CREATE"=>"Create Client"}
[1m[36mUser Load (0.7ms)[0m [1mSELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1[0m
Unpermitted parameters: clientassignmentable
[1m[35m (0.3ms)[0m BEGIN
[1m[36m (0.2ms)[0m [1mROLLBACK[0m
I keep getting this unpermitted parameters: clientassignmentable. But I've already whitelisted in the my controller as one of the strong params so why is it complaining???
Surely this couldn't be hard to figure out if you know what's the correct association laid out in front of you already?
What else could I be missing?!?! I've been on this for the whole day, looking very confused....

You should write
fields_for :client_assignment
and change this accordingly everywhere else you use :clientassignmentable
further reading:
http://guides.rubyonrails.org/form_helpers.html#understanding-parameter-naming-conventions
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
To solve the problem with fields not rendering in new form, in controller you need to do
#client_individual.build_client_assignment

I think where I did go wrong with this.....
It's so embarrassing after reading the basics of setting up polymorphic associations, I realised my polymorphic association was completely utterly wrong!!
As a consequence of that, I wasted almost 2 days of trying to save the form's data when clearly my initial steps of database design was utterly wrong! What a complete utter waste!!!
ARRRGGGH!!!!!
Now I need to go back to the drawing board and find what how to revert it!!!

Related

Rails 5 - form_with - fields_for assign nested attributes upon creation of parent items.

I'm new to rails or coding in general, trying to learn by creating a simple app, I'm not sure if it's silly to ask, but I tried really finding a concrete answer to the problem and failed each time for days, I would appreciate if someone can help me on this;
I have a "Plane" model which has "PlaneModel", "Xroutes" and "Staffs" attributes. Each 3 attrs, are pre-created, have their own tables, and upon a new instance of plane creation you just select through available items via "collection_select". Code for Plane.rb;
class Plane < ApplicationRecord
belongs_to :plane_model
belongs_to :xroute
has_many :staffs
accepts_nested_attributes_for :staffs
end
Now, because of the db relations, Planes table have a Planemodel_id, Xroute_id but not Staff_id. There lies the problem, because when I try to create an instance of a plane, "collection_select" easily works for the first 2 attrs, assigning Planemodel_id and Xroute_id respectively. For staffs though what I want to achieve is, after creation of each plane, assigning that plane_id on the corresponding staff row in Staffs table. Code for Staff.rb;
class Staff < ApplicationRecord
belongs_to :hub, optional: true
belongs_to :plane, optional: true
end
Below is the _form for planes;
<%= form_with(model: plane, local: true) do |form| %>
<div class="control-group">
<%= form.label :plane_model_id %>
<div class="controls">
<%= collection_select( :plane, :plane_model_id, PlaneModel.all, :id, :name, {}, { :multiple => false } ) %>
</div>
</div>
<div class="control-group">
<%= form.label :xroute_id %>
<div class="controls">
<%= collection_select( :plane, :xroute_id, Xroute.all, :id, :name, {}, { :multiple => false } ) %>
</div>
</div>
<%= form.fields_for :staffs do |staff| %>
<div class="control-group">
<%= staff.label :staff_id %>
<div class="controls">
<%= staff.collection_select( :plane_id, Staff.all, :id, :name, { :multiple => false } ) %>
</div>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Inside the plane controller new & create functions and params function to whitelist attributes;
def new
#plane = Plane.new
3.times { #plane.staffs.build }
end
def create
#plane = Plane.new(plane_params)
respond_to do |format|
if #plane.save
format.html { redirect_to #plane, notice: 'Plane was successfully created.' }
format.json { render :show, status: :created, location: #plane }
else
format.html { render :new }
format.json { render json: #plane.errors, status: :unprocessable_entity }
end
end
end
def plane_params
params.require(:plane).permit(:plane_model_id, :xroute_id, staffs_attributes: [:id, :staff, :plane_id])
end
Here is the visual representation of what's happening;
Form for creating new plane instance, PlaneModel, Xroute and 3 staff members are selected from dropdown lists.
When submitted, a new instance is successfully created.
Here is the index page for Planes, as you see PlaneModel and Xroute are there.
When I go to staffs page though, instead of putting created plane's id in the selected staffs "plane_id" field, it just throws additional 3 staff instances without any more information on it.
Staff index page showing, newly created empty staff instances.
Like I said maybe it's a simple thing but I'm missing something for sure, appreciate all the help & suggestions.
Thanks,
accepts_nested_attributes_for :staffs allows you to create a staffs instances together with a plane, inside the same form. But as I can see you want only to select existing staff, am I right? You need to change you form:
<div class="control-group">
<%= form.label :staff_ids %>
<div class="controls">
<%= form.collection_select( :staff_ids, Staff.all, :id, :name, {}, { multiple: true } ) %>
</div>
</div>
params:
def plane_params
params.require(:plane).permit(:plane_model_id, :xroute_id, staff_ids: [])
end
and remove 3.times { #plane.staffs.build } from new action

Unknown parameter in Rails 5.1 strong parameters issue

So my reconciliation model looks like this:
class Reconciliation < ApplicationRecord
belongs_to :location
belongs_to :company
has_and_belongs_to_many :inventory_items
accepts_nested_attributes_for :inventory_items, allow_destroy: true
end
My InventoryItem model looks like this:
class InventoryItem < ApplicationRecord
belongs_to :product
belongs_to :location, inverse_of: :inventory_items
has_and_belongs_to_many :reconciliations
end
In my ReconciliationsController, this is what my reconciliation_params looks like:
def new
#location = Location.find(params[:location_id])
#reconciliation = #location.reconciliations.new
#inventory_items = #location.inventory_items
#start_index = 0
#next_index = #start_index + 1
end
def reconciliation_params
params.require(:reconciliation).permit(:inventory_item_id, :location_id, :display_id, :inventory_items,
inventory_items_attributes: [:id, :quantity_left, :quantity_delivered, :_destroy]
)
end
This is the relevant section of my routes.rb:
resources :locations, shallow: true do
resources :inventory_items
resources :reconciliations
end
This is my views/reconciliations/_form.html.erb:
<%= simple_form_for #reconciliation, url: :location_reconciliations do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :location_id, as: :hidden %>
<%= f.simple_fields_for :inventory_item do |inventory| %>
<%= inventory.input :quantity_left %>
<%= inventory.input :quantity_delivered %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit, "Update", class: "btn btn-primary" %>
</div>
<% end %>
This is my app/views/reconciliations/new.html.erb:
<% if params[:next].nil? %>
<%= render 'form', reconciliation: #reconciliation, inventory_item: #inventory_items[#start_index] %>
<% else %>
<%= render 'form', reconciliation: #reconciliation, inventory_item: #inventory_items[#next_index] %>
<% end %>
This is my log when I try to create a reconciliation object:
Started POST "/locations/2/reconciliations" for 127.0.0.1 at 2018-03-24 23:16:33 -0500
Processing by ReconciliationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JZvhwloo0+XM9bmptxXGfnDw==", "reconciliation"=>{"location_id"=>"2", "inventory_item"=>{"quantity_left"=>"1", "quantity_delivered"=>"170"}}, "commit"=>"Update", "location_id"=>"2"}
Unpermitted parameter: :inventory_item
Location Load (0.9ms) SELECT "locations".* FROM "locations" WHERE "locations"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.6ms) BEGIN
(0.7ms) ROLLBACK
Rendering reconciliations/new.html.erb within layouts/application
InventoryItem Load (1.0ms) SELECT "inventory_items".* FROM "inventory_items" WHERE "inventory_items"."location_id" = $1 [["location_id", 2]]
Product Load (1.0ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
Rendered reconciliations/_form.html.erb (45.9ms)
Rendered reconciliations/new.html.erb within layouts/application (66.8ms)
Rendered shared/_navbar.html.erb (1.3ms)
Completed 200 OK in 202ms (Views: 115.1ms | ActiveRecord: 29.1ms)
I have tried simply adding :inventory_item to my params.require(:reconciliation).permit(..), but that doesn't work.
What am I missing?
Edit 1
When I checked the HTML for the inputs on my form, within the simple_fields_for, the HTML seems to be fine:
<input class="string required" type="text" name="reconciliation[inventory_item][quantity_left]" id="reconciliation_inventory_item_quantity_left">
Edit 2
When I change the simple_fields_for call to be plural, i.e. :inventory_items, rather than :inventory_item like this:
That entire portion of the form disappears altogether.
This is what the HTML looks like:
<div class="form-inputs">
<div class="input hidden reconciliation_location_id"><input class="hidden" type="hidden" value="2" name="reconciliation[location_id]" id="reconciliation_location_id"></div>
</div>
This is how the HTML looks when that simple_field_for :inventory_item is singular:
<div class="form-inputs">
<div class="input hidden reconciliation_location_id"><input class="hidden" type="hidden" value="2" name="reconciliation[location_id]" id="reconciliation_location_id"></div>
<div class="input string required reconciliation_inventory_item_quantity_left"><label class="string required" for="reconciliation_inventory_item_quantity_left"><abbr title="required">*</abbr> Quantity left</label><input class="string required" type="text" name="reconciliation[inventory_item][quantity_left]" id="reconciliation_inventory_item_quantity_left"></div>
<div class="input string required reconciliation_inventory_item_quantity_delivered"><label class="string required" for="reconciliation_inventory_item_quantity_delivered"><abbr title="required">*</abbr> Quantity delivered</label><input class="string required" type="text" name="reconciliation[inventory_item][quantity_delivered]" id="reconciliation_inventory_item_quantity_delivered"></div>
</div>
I have tried simply adding :inventory_item to my params.require(:reconciliation).permit(..), but that doesn't work.
If you want permit inventory_item, you must specify its structure, because it is not a simple field, but a hash:
def reconciliation_params
params.require(:reconciliation).permit(:location_id, :display_id, inventory_item: [:id, :quantity_left, :quantity_delivered] )
end
By looking at your log, you are not passing the inventory_item_id, which might be needed to update this specific item:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JZvhwloo0+XM9bmptxXGfnDw==",
"reconciliation"=>{"location_id"=>"2", "inventory_item"=>
{"quantity_left"=>"1", "quantity_delivered"=>"170"}},
"commit"=>"Update", "location_id"=>"2"}
You could add it as a hidden field in the nested form.
Form of association should be plural f.simple_fields_for :inventory_items.
You should initialize a new inventory_item object in the new controller's action
def new
#reconciliation = Reconciliation.new
# you can create as many new items as you want
#reconciliation.inventory_items.build
end
If you want to dynamically add items to the form i advise you to use https://github.com/nathanvda/cocoon
BUT it looks like you want to add existing inventory_item to a new reconciliation, you better use has_many through assocations http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many
Its easier to add a join model objects with neccessary fields and associations.
Another advise: do not send local variable to partial if you are using instance variable in this partial
# render partial
render 'form', reconciliation: #reconciliation
# partial with form for local variable
simple_form_for reconciliation
and i think your form partial will not work for the edit action because of hardcoded url, you can pass url in a variable:
# new html
render 'form', reconciliation: #reconciliation, url_var: location_reconciliations(#location)
# edit
render 'form', reconciliation: #reconciliation, url_var: reconciliations(#reconciliation)
# form
simple_form_for reconciliation, url: url_var

form_for data not submitting to mysql database

In my view page I have a form like
<%= form_for (#writereview), url:
createreview_path(#writereview),:class=>"form-horizontal",method: :post do |f| %>
<div class="form-inline">
<div class="form-group">
<label for="clean">Cleanliness</label>
<%= f.select(:clean, [['1', 1], ['1.5', 2], ['2',3],['2.5',4],['3',5],['3.5',6],['4',7],['4.5',8],['5',9]],{},{ :class => "form-control"}) %>
</div>
<div class="form-group">
<label for="clean">Food</label>
<%= f.select(:food, [['1', 1], ['1.5', 2], ['2',3],['2.5',4],['3',5],['3.5',6],['4',7],['4.5',8],['5',9]],{},{ :class => "form-control"}) %>
</div>
and in controller action -
def create
#writereview = WriteReview.new(params[:writereview])
if #writereview.save!
redirect_to root_path
end
end
private
def user_params
params.require(:writereview).permit(:clean,:food,:locality,:behavior,:amenity,:likes,:dislikes,:comment)
end
Use following code:
def create
#writereview = WriteReview.new(user_params)
if #writereview.save!
redirect_to root_path
end
end
You aren't using your santization, but that isn't causing your problem.
def create
#writereview = WriteReview.new(write_params)
end
private
def write_params
params.require(:writereview).permit(:clean,:food,:locality,:behavior,:amenity,:likes,:dislikes,:comment)
end
I'd look at the model, the database schema. Your model does not know about clean, food, locality, behavior, amenity, likes, dislikes, etc. I know this because:
INSERT INTO write_reviews (created_at, updated_at) VALUES ('2015-12-06 16:04:01', '2015-12-06
There are no fields that are being recorded to the record, Your schema is not aware of these fields, so nothing is recorded.

Rails 4 nested attribute hash keys showing as unpermitted

I have a Category model and a Standard model. A category has many standards through a "join table" model CategoryStandard. In my view, I have a form where I can edit the category description, and then add or remove standards from that category. So, my nested attributes are for :category_standards, because I'm not editing the standard itself, just adding or removing relationships, if that makes sense.
Here's the important part of the view:
<%= form_for(#category) do |f| %>
...
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<%= label_tag nil, "Standards in this Category" %>
<div id="standard-list">
<%= f.fields_for :category_standards do |ff| %>
<div class="field">
<%= ff.object.standard.number_with_exceptions %>
<%= ff.hidden_field :standard_id %>
<%= ff.hidden_field :_destroy %>
<%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, "", class: "del-std-btn", title: "Remove standard from category" %>
</div>
<% end %>
<div class="hidden" id="std-add-new-template">
<div class="field">
<%= f.fields_for :category_standards, CategoryStandard.new, child_index: "new_id" do |ff| %>
<%= ff.collection_select :standard_id, #standards - #category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
<% end %>
</div>
</div>
</div>
...
<% end %>
There's some jQuery under the hood to manipulate the "rows", but that works fine and I don't think it's part of my problem, so I'll omit it.
In my Category model, I have:
class Category < ActiveRecord::Base
has_many :category_standards, dependent: :destroy
has_many :standards, through: :category_standards
validates :description, presence: true,
uniqueness: true
accepts_nested_attributes_for :category_standards, allow_destroy: true, reject_if: proc { |attributes| attributes['standard_id'].blank?}
end
And in my Categories controller, I have:
def category_params
params.require(:category).permit(:description, category_standards_attributes: [:id, :standard_id, :_destroy])
end
But when I try to add a standard to a category, I get these lines in my server log (reformatted in the hopes of making it more readable):
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"***********",
"category"=>{
"description"=>"Drinking Water System Components",
"category_standards_attributes"=>{
"0"=>{
"standard_id"=>"2",
"_destroy"=>"false",
"id"=>"1"
},
"new_id"=>{
"standard_id"=>""
},
"1424899001814"=>{
"standard_id"=>"1"
}
}
},
"commit"=>"Save Changes",
"id"=>"2"
}
User Load (5.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Category Load (4.0ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1 [["id", "2"]]
Unpermitted parameters: 0, new_id, 1424899001814
(4.0ms) BEGIN
Category Exists (6.0ms) SELECT 1 AS one FROM "categories" WHERE ("categories"."description" = 'Drinking Water System Components' AND "categories"."id" != 2) LIMIT 1
SQL (6.0ms) UPDATE "categories" SET "description" = $1, "updated_at" = $2 WHERE "categories"."id" = 2 [["description", "Drinking Water System Components"], ["updated_at", Wed, 25 Feb 2015 21:16:44 UTC +00:00]]
It updates the description field just fine, but what's up with the Unpermitted parameters? My attributes hash comes out just like the example in the Rails Guide on nested forms, and it even says "The keys of the :addresses_attributes hash are unimportant, they need merely be different for each address." And yet it's the keys that are getting denied for me.
Where have I gone wrong? Thanks!
Figured it out, after a lot of reading. The missing piece was here.
Hashes with integer keys are treated differently and you can declare the attributes as if they were direct children. You get these kinds of parameters when you use accepts_nested_attributes_for in combination with a has_many association:
# To whitelist the following data:
# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}
params.require(:book).permit(:title, chapters_attributes: [:title])
The important part of that was "Hashes with integer keys". My hash keys were passing as "0", "new_id", "1240934304343". It isn't important that I use "new_id", because that's just a placeholder value that gets changed in my jQuery when new rows are added. Only the template row retains that value, which is fine, because it gets filtered out by my reject_if clause.
But the fact that "new_id" isn't an integer apparently was the thing that was mucking it all up. So I changed it to "-1", which Rails accepts (even though it is still filtered out by reject_if, as it should be).
<div class="hidden" id="std-add-new-template">
<div class="field">
<%= f.fields_for :category_standards, CategoryStandard.new, child_index: "-1" do |ff| %>
<%= ff.collection_select :standard_id, #standards - #category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
<% end %>
</div>
</div>
Your attribute keys don't seem to match what you are expecting in your strong parameters, "new_id" and "1424899001814" certainly will not be permitted.
"new_id"=>{
"standard_id"=>""
},
"1424899001814"=>{
"standard_id"=>"1"
}
I suspect the way you are constructing your form is invalid. Try breaking it down to the simplest working form.. like:
<div id="standard-list">
<%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, "", class: "del-std-btn", title: "Remove standard from category" %>
<div class="hidden" id="std-add-new-template">
<div class="field">
<%= f.fields_for :category_standards do |ff| %>
<%= ff.object.standard.number_with_exceptions %>
<%= ff.hidden_field :standard_id %>
<%= ff.hidden_field :_destroy %>
<%= ff.collection_select :standard_id, #standards - #category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
<% end %>
</div>
</div>
</div>
The intention is to have only one nested form, and by stripping it down create only one nested hash.
"category_standards_attributes"=>{
"0"=>{
"standard_id"=>"2",
"_destroy"=>"false",
"id"=>"1"
}
}
What happens with this?

setting up search model to wrap around user model

I was finally able to come up with some sort of structure for my advanced search. The idea is to have a search that pulls data from the User model. So I built a search model which will be a kind of wrapper around the user model. For example: when I say Search.search_for(options), in the search_for method it will use the options to search on the User model and return the results, which you can display on the page. I came up with this method as I was told I have to duplicate the values, but I figured I just need to make Search call the underlying (already existing) User model.
So I have a view from where users can search. I have to collect all the options they have specified that they want to search on (gender, age, zip code, do they have kids, religion and ethnicity). Collect the options and submit the form to SearchController.
I have the concept down, but am struggling with the execution since I am new to Rails. The code below is essentially what I have (minus User model as it's filled with other parts from app). I'm not sure how to finish the rest of the coding to pull this off.
Searches_controller:
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search
else
render 'new'
end
end
def show
#search = Search.find(params[:id])
#users = Users.search(params)
end
end
search model:
attr_accessible :age, :children, :ethnicity, :gender, :religion, :zip_code
def users
#users ||= find_users
end
def self.search(params)
end
private
def find_users
users = User.order(:id)
users = users.where(gender: gender) if gender
users = users.where(:ethnicity => ethnicity) if ethnicity
end
new.html (advanced search page):
<%= form_for #search do |f| %>
<div class="field">
<%= f.label :gender %><br />
<%= f.select :gender, ['man', 'woman'], :include_blank => true %>
</div>
<div class="field">
<%= f.label :zip_code %><br />
<%= f.text_field :zip_code %>
</div>
<div class="field">
<%= f.label :children %><br />
<%= f.select :children, ['Yes, they live with me', 'I want kids now', "I want one someday", "Not for me"], :include_blank => true %>
</div>
<div class="field">
<%= f.label :religion %><br />
<%= f.select :religion, ["Agnostic", "Atheist", "Christian", "Catholic", "Buddhist", "Hindu", "Jewish", "Muslim", "Spiritual without affiliation", "Other", "None", "Prefer not to say"], :include_blank => true %>
</div>
<div class="field">
<%= f.label :ethnicity %><br />
<%= f.select :ethnicity, ["Asian", "Biracial", "Indian", "Hispanic/Latin", "Middle Eastern", "Native American", "Pacific Islander", "White", "Other"], :include_blank => true %>
</div>
<div class="actions"><%= f.submit "Search" %></div>
<% end %>
show.html (view to show results):
<%= render #search.users %>
development log:
Started POST "/searches" for 127.0.0.1 at 2013-05-06 14:00:54 -0400
Processing by SearchesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Pher65dG6gRU9NGgv2q1ot0cfjq+MELgXE6dOtvcrY0=", "search"=>{"gender"=>"", "zip_code"=>"", "children"=>"", "religion"=>"", "ethnicity"=>"Asian"}, "commit"=>"Search"}
[1m[36m (0.2ms)[0m [1mBEGIN[0m
[1m[35mSQL (107.5ms)[0m INSERT INTO `searches` (`age`, `children`, `created_at`, `ethnicity`, `gender`, `religion`, `updated_at`, `zip_code`) VALUES (NULL, NULL, '2013-05-06 18:00:54', 0, NULL, NULL, '2013-05-06 18:00:54', '')
[1m[36m (60.1ms)[0m [1mCOMMIT[0m
Redirected to http://localhost:3000/searches/25
Completed 302 Found in 276ms (ActiveRecord: 167.7ms)
Started GET "/searches/25" for 127.0.0.1 at 2013-05-06 14:00:54 -0400
Processing by SearchesController#show as HTML
Parameters: {"id"=>"25"}
[1m[35mSearch Load (0.5ms)[0m SELECT `searches`.* FROM `searches` WHERE `searches`.`id` = 25 LIMIT 1
[1m[36mUser Load (73.2ms)[0m [1mSELECT `users`.* FROM `users` WHERE `users`.`zip_code` = '' AND `users`.`ethnicity` = '0' ORDER BY id[0m
Rendered collection (0.0ms)
Rendered searches/show.html.erb within layouts/application (81.4ms)
[1m[35mUser Load (0.6ms)[0m SELECT `users`.* FROM `users` WHERE `users`.`auth_token` = 'LTzif2q6921TM4pQzfmEGg' LIMIT 1
Completed 200 OK in 309ms (Views: 224.6ms | ActiveRecord: 74.3ms)
Ok, first of all make sure that the User model and the Search model have exactly the same attributes, so gender, zip_code, children, religion and ethnicity.
The only time you would need to have differing column names is if you want to do something a bit more "complex", such as searching a range, for example if the users table has age, then the searches table might have min_age and max_age.
Searches controller:
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search
else
render 'new'
end
end
def show
#search = Search.find(params[:id])
#users = #search.users
end
Searches model:
def users
users = User.order(:id)
users = users.where(gender: gender) if gender
users = users.where(zip_code: zip_code) if zip_code
users = users.where(children: children) if children
users = users.where(religion: religion) if religion
users = users.where(ethnicity: ethnicity) if ethnicity
users
end
In app/views/searches/show.html.erb:
<h1>Search results</h1>
<%= render #users %>
And make sure that you have the partial app/views/users/_user.html.erb with something like:
<p><%= user.name %> - <%= user.email %></p> # or whatever
Now go to the search form and create a new search object, get the ID from the URL (lets say it's 34).
Then go into rails console and do:
#search = Search.find(34)
# should return something like: gender: "male", zip_code: nil, children: "Yes"..
# then try the users method:
#search.users
Not sure why your view isn't working. Make sure you have #users = #search.users in your searches controller under the show action, then go to views/searches/show.html.erb and do:
<% #users.each do |user| %>
<p><%= user.name %></p>
<% end %>
And see if that works.

Resources