Rails controller saving from different table - ruby-on-rails

I'm new to rails to bear with me.
This concerns two of my models: Product and Manufacturer.
When creating a new product the user can select which manufacturer the product belongs to from a drop down list. The problem is that I can't get this manufacturer to save.
I know I have to add some code to the controller and I've tried various things but to no avail.
Here's the view:
<h1>New Product</h1>
<%= form_for(#product) do |f| %>
<div>
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :market_price, 'Market Price' %>
<%= f.text_field :market_price %>
</div>
<div>
<%= f.label :sell_price, 'Sell Price' %>
<%= f.text_field :market_price %>
</div>
<div>
<%= f.label :stock_level, 'Stock Level' %>
<%= f.text_field :stock_level %>
</div>
<div>
<%= f.label :manufacturer, 'Manufacturer' %>
<%= f.collection_select(:manufacturer, Manufacturer.all, :id, :name, prompt: true) %>
</div>
<div>
<%= f.label :location, 'Location' %>
<%= f.collection_select(:location, Product.all, :id, :location, prompt: true) %>
</div>
<br> </br>
<div>
<%= f.submit "Create Product" %>
</div>
And here's part of the controller:
def create
#product = Product.new(params[:product].permit(:name, :market_price, :sell_price, :stock_level, :location))
#product.save
flash[:notice] = 'Product Created'
redirect_to #product
end
end
After hours of trying several nesting methods, I still can't get this to work.
Surely it's very common to save fields from various models on one page???

You would usually nest your routes, so that products were within manufacturer:
resources :manufacturer do
resources :products
end
Then your form would be a form for an array:
form_for([#manufacturer, #product]) do |f|
f.hidden_field :manufacturer_id, value: #manufacturer.id
This allows you to pass in the ID of both the manufacturer and the product.
Now in your controller you can use something like the following, provided the associations are set up, such as product belongs_to :manufacturer and manufacturer has_many :products
#manufacturer = find(params[:manufacturer_id])
#product = #manufacturer.products.create()

In your form, you need to have a field for manufacturer_id, not manufacturer. You would change f.collection_select(:manufacturer to be f.collection_select(:manufacturer_id.
Then, in your controller, you need to add manufacturer_id to the list of parameters you are permitting in your permit method call. So it would be Product.new(params[:product].permit(:name, :market_price, :sell_price, :stock_level, :location, :manufacturer_id)).

Related

How to organize form_for helper to show data from different models?

I have three models PriceGroup, PriceGroupLine and Item.
PriceGroup has fields - :id and :name.
Item has fields - :id and :name.
PriceGroupLine has fields - :id, :price_group_id, :item_id, :price
Associations:
PriceGroup has_many PriceGroupLines
Item has_one PriceGroupLine
PriceGroupLine belongs_to Item
PriceGroupLine belongs_to PriceGroup
I need to insert in PriceGroupLine model lines from PriceGroup show view. How I should organize form_for helper if I need to insert in PriceGroupLine:
item_id - items list organized with collection_select helper
price_group_id
price of item
I am using this code on view but I am not able to insert it:
<%= form_for #price_group, html: {class: "form-inline"} do |f| %>
<div class="form-group">
<label>Товар</label>
<%= f.collection_select(:id, Item.all, :id, :name, {}, {class: "form-control"}) %>
</div>
<div class="form-group">
<label>Цена</label>
<%= f.text_field :price, class: "form-control" %>
</div>
<%= f.submit "Добавить", class: "btn btn-default" %>
<% end %>
And one more question about the associations. At the beginning i had associations like:
PriceGroup has_many PriceGroupLines
PriceGroupLine has_many Items
Item belongs_to PriceGroupLine
PriceGroupLine belongs_to PriceGroup
But this associations didn't work correctly when i tried to get :name field from Item model like this:
<% #price_group.price_group_lines.each do |price_group_line| %>
<%= price_group_line.item.try(:name) %>>
<%= price_group_line.price %>
<% end %>
I changed associations and all worked. Now PriceGroupLine belongs_to :item and Item has_one :price_group_line. But it is logical to assume that ITEM is belongs to PriceGroupLine (PRICEGROUPLINE contains ITEM). Am I wrong?
UPDATE
Almost there :)) Here is a screenshot of my form.
Code of show view PriceGroup:
<%= form_for #price_group, html: {class: "form-inline"} do |f| %>
<%= f.fields_for :price_group_lines do |price_group_line| %>
<%= render partial: "price_group_line_fields", locals: { f: price_group_line } %>
<% end %>
.links
<%= link_to_add_association "Добавить", f, :price_group_lines %>
<% end %>
Code of partial:
<div class="form-group">
<label>Товар</label>
<%= f.collection_select(:item_id, Item.all, :id, :name, {}, {class: "form-control"}) %>
</div>
<div class="form-group">
<label>Цена</label>
<%= f.text_field :price, class: "form-control" %>
</div>
In footer Helper makes fields for each record in database. I need only items list to select a proper item and blank field PRICE to enter the price. And some word .link appears.
First of all you will be needing nested form for this. Using the nested form you will be able to insert multiple price_group_lines from price_group form. I would suggest you to use gem like Cocoon for this. What you need to do is:
In form:
<%= form_for #price_group, html: {class: "form-inline"} do |f| %>
<%= f.fields_for :price_group_lines do |price_group_line| %>
<%= render 'price_group_line_fields', f: price_group_line %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add price group line', f, :price_group_lines %>
</div>
<% end %>
In price_group_line_fields:
<div class="form-group">
<label>Товар</label>
<%= f.collection_select(:item_id, Item.all, :id, :name, {}, {class: "form-control"}) %>
</div>
<div class="form-group">
<label>Цена</label>
<%= f.text_field :price, class: "form-control" %>
</div>
And in your controller you will need to allow this in strong parameters:
params.require(:price_group).permit(:name, :price_group_lines_attributes => [:item_id, :price])
And in you PriceGroup model you need to write:
accepts_nested_attributes_for :price_group_lines
This is just a example and rough code using cocoon gem, you can find more options by searching rails nested form. The code may throw error which you can easily fix.
Hope this helps.

Saving to an enum field in Rails 4.1.1

I am trying to make use of the enum feature that has been added to Rails. I had been waiting for this for quite some time.
Here is how I set it up:
Product model:
enum category: [:t_shirt, :hoodie, :jacket]
Product controller:
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product, notice: 'Product was successfully created.' }
else
render :new
end
end
def product_params
params.require(:product).permit(:title, :description, :category, :price)
end
New form
<%= form_for(#product) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= f.select :category, Product.categories, include_blank: "Select a category" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This correctly populates the drop-down field in my form with the values of the different enum options which I have defined in the model.
However, when I submit the form having selected one of the categories from the drop-down, it gives me an error:
'0' is not a valid category
Even though my category field is an integer field and '0' is the correct integer associated with the category I selected in my form.
It also highlights the following line from the create method in my Product controller as the place where the error occured:
#product = Product.new(product_params)
I am completely confused as to why this is happening. Would really appreciate some help.
Thank you.
Instead:
<%= f.select :category, Product.categories, include_blank: "Select a category" %>
Try:
<%= f.select :category, Product.categories.keys, include_blank: "Select a category" %>
Explain:
In Product.categories hash {"t_shirt"=>0, "hoodie"=>1, "jacket"=>2} but in Product.categories.keys array what you need ["t_shirt", "hoodie", "jacket"] for select helper.

Ruby on Rails, two models in one form

I have two very similar models Pretreatment and Diagnosis, that belong to the model Patient:
class Pretreatment < ActiveRecord::Base
belongs_to :patient
attr_accessible :content
end
class Diagnosis < ActiveRecord::Base
belongs_to :patient
attr_accessible :content
end
class Patient < ActiveRecord::Base
attr_accessible :age, :name, :city, :street, :number
has_many :anamneses
has_many :befunds
end
On the Patient show page I'm displaying two forms, one for the Preatreatment and another for the Diagnosis:
<%= form_for([#patient, #patient.preatreatments.build]) do |f| %>
<div class="field">
<%= f.label :conten %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= form_for([#patient, #patient.diagnosiss.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My question is how can I bring the two forms together, so that the user only has to press once the submit button? Im not sure but I think nested attributes is not the right thing to handle it, maybe thefields_for` tag?
Update I tried to use fields_for tag:
<%= form_for([#patient, #patient.pretreatment.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<%= fields_for([#patient, #patient.diagnosiss.build]) do |u| %>
<div class="field">
<%= u.label :content %><br />
<%= u.text_field :content %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But I get the error:
undefined method `model_name' for Array:Class in <%= fields_for([#patient,#patient.befunds.build]) do |u| %>
Use fields_for for the associated models.
There should be no square brackets arround the parameters of fields_for
In your code example, I cannot find the relation between Patient and Diagnosis, and the plural of diagnosis is diagnoses, you can specify this in config/initializers/inflections.rb:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'diagnosis','diagnoses'
end
So your Patient model should contain
class Patient < ActiveRecord::Base
attr_accessible :age, :name, :city, :street, :number
has_many :diagnoses
end
And you can write in your form:
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<%= fields_for(#patient, #patient.diagnoses.build) do |u| %>
<div class="field">
<%= u.label :content %><br />
<%= u.text_field :content %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
You can achieve that using nested attributes :
patient.rb
class Patient < ActiveRecord::Base
attr_accessible :age, :name, :pretreatments_attributes, :diagnosiss_attributes
has_many :pretreatments
has_many :diagnosiss
accepts_nested_attributes_for :pretreatments
accepts_nested_attributes_for :diagnosiss
end
patients_controller.rb
def show
#patient = Patient.find(params[:id])
#patient.pretreatments.build
#patient.diagnosiss.build
respond_to do |format|
format.html # show.html.erb
format.json { render json: #patient }
end
end
patients/show.html.erb:
<%= form_for #patient do |f|%>
<h3>Pretreatments:</h3>
<%= f.fields_for :pretreatments do |field| %>
<%= field.label "Content" %></div>
<%= field.text_field :content %>
<% end %>
<h3>Diagnosis:</h3>
<%= f.fields_for :diagnosiss do |field| %>
<%= field.label "Content" %></div>
<%= field.text_field :content %>
<% end %>
<%=f.submit %>
<% end %>
And that all
There are a few ways of doing this:
The way the fields_for works is you have a form_for for the parent model and within it you can place fields_for the models which belong to the parent. A good example is given in the Rails Docs
A simple and more extensible option over time but not very "semantic" one would be to use JavaScript/JQuery. You could trigger $("form #new_pretreatments").submit(); and the same for the Diagnosis once a button is clicked.
Maybe instead you could have just one model to store them both. For example a model called Disease. Each time a patient gets a disease a new one is created and it has columns for each the Diagnosis and Pretreatment. It's probably the best option instead of adding another model for each bit of info a patient can have.
You can use fields_for for the second model, which works like form_for but doesn't generate the form tags. See the docs.
There are some gems available for nested forms. one of them is awesome_nested_fields. I haven't used this earlier but that shows good code in documentation. Another one is simple_form.
Hope that helps!!!

How to store select information

I am a bit unsure on how to store the data from a collection_tag.
Here the model I am working on
Customer GenderManager Genders
Id Id Id
First Customer_id Description
Last Gender_id
Email Created_at
Password
Here the relationship
Customer::class
has_many :gender_managers
accepts_nested_attributes_for :gender_managers, allow_destroy: :true
Gender::class
has_many :gender_managers
GenderManagers:class
belongs_to :customer_id
belongs_to :gender_id
The form I have currently his has follow
<%= form_for #customer do |f| %>
<div class="customer_edit_error_message">
<%= render 'shared/error_messages' %>
</div>
<fieldset>
<div class="customer_edit_name">
<%= f.label :first_name %>
<%= f.text_field :first_name %>
</div>
<div class="customer_edit_middle_name">
<%= f.label :middle_name %>
<%= f.text_field :middle_name %>
</div>
<div class="customer_edit_last_name">
<%= f.label :last_name %>
<%= f.text_field :last_name %>
</div>
<div class="customer_edit_gender">
<%= f.collection_select(:customer, :id, Gender.all, :id, :description) %>
</div>
<div class="customer_edit_button">
<%= f.submit "Save changes", class: "button_accept" %>
</div>
<% end %>
In customerController not sure if I must do something. I know its not right its giving me errors. Its the form that worry me on the select like
Thanks in advance.
I recommend you add an attribute gender to your Customer model. If you want to keep track of changes you can create a GenderHistory model and do something like this in Customer:
class Customer < ActiveRecord::Base
has_many :gender_histories
before_update :track_gender
def track_gender
self.gender_histories.create(:gender => self.gender) if self.gender_changed?
end
end
Your Genders and GenderManager models are not necessary.

How to create only the field_for object in a nested form

I have a nested form where users can create any amount of products (<%= f.fields_for :products do |product| %>) and at the same time also create a location (nested_form_for #location) to put those products. But instead, what i want is users to select a location and only be allowed to create the products. I don't want to create the locations at all on this form. The form also should not give a location per product but only one location for all of them.
How would you make the form select a location and create products only ?
This form creates both the location and X amount of products:
<%= nested_form_for #location, :validate => true do |f| %>
# This will turn into selecting of predefined locations and not creating
# the location as you see below this line
<%= f.label :business_name, "Business Name" %><br />
<%= f.text_field :business_name %>
<%= f.label :address %><br />
<%= f.text_field :address %>
<%= f.label :phone_number, "Phone Number" %><br />
<%= f.text_field :phone_number %>
<%= f.label :website %><br />
<%= f.text_field :website %>
</div>
<%= f.fields_for :products do |product| %>
<%= product.label :name %>:<br>
<%= product.text_field :name %>
<%= product.label :price %>:<br>
<%= product.text_field :price %>
<%= product.link_to_remove "Remove" %> # Removes a product field
<% end %>
</div>
<p><%= f.link_to_add "Add Product", :products %></p> # Ajax add product field
<div class="actions">
<%= f.submit %>
</div>
<% end %>
If I understand you correctly, you want users to choose from a list of locations, and then add products to that location. These locations are predefined somewhere (I'm getting in your db_seed) in your application. Assuming those two things, then the question to your answer is just have a normal form_for #product and inside your form you will have a select with the option objects being the location where they users can choose.
<%= form_for #product do |p| %>
<%= p.label :name %>:<br>
<%= p.text_field :name %>
<%= p.label :price %>:<br>
<%= p.text_field :price %>
<%= select :product, :location_id, Location.all.collect {|l| [ l.business_name, l.id ] } %>
<% end %>
and in your controller you can just get the location with params[:product][:location_id] to retrieve the location the user selected and do your normal database stuff. Hopefully that's clear enough for you.
If you want to create more than one product at a time, you need to use a form_tag and generate your own helpers to add/remove products. This is more difficult but creates a better user experience.

Resources