Rails: Automatically assign parameters in a form - ruby-on-rails

I'm creating a Rails app which allows users to create different animals and assign them attributes.
One of the attributes they an assign is "range". There are currently four types of ranges and each range has a different id:
land - id:1
sea - id:2
air - id:3
underground - id:4
Range has its own controller, model, and view in my app.
Each animal can be created with an id, a name, and a range. But I also have a column entitled range_id, which is what I am using to tie the animal back to the range.
{name:"cat", range:"land"}
#ids are assigned automatically
For views/animals/new.html.erb, I include a form that asks for each animal's attributes.
<h1> Add Animal </h1>
<%= form_for :animal, url: animals_path do |f| %>
<p>
<%= f.label :name %> <br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :range %> <br>
<%= f.select :range, ['land', 'sea', 'air', 'underground'], :prompt => 'Select One' %>
</p>
<p>
<%= f.submit %>
<p>
<% end %>
My problem is, I need to automatically assign a range_id to each new Animal based on the range. So, if a user created "whale" with a range "sea", that Animal also needs to have a range_id of 2.
How can I do this automatic assignment? Here is my current animal_params method in animals_controller.rb:
private def animal_params
params.require(:animal).permit(:id, :name, :range, :range_id)
end
Should range_id be included there, or what would be the easiest way to alter it automatically?

You can find an answer in the documentation here - you shouldn't be hardcoding these values into your view, and should instead be using ActiveRecord to populate the values for you.

<%= f.select :range_id, options_for_select(Range.all.collect { |range| [range.name, range.id]}, f.object.range_id), {}, class: 'form-control', include_blank: 'Please Select' %>
You can get the reference from here.
I assume that you have a model named Range which has two attributes - name, id. The select box above will show all the ranges in the dropdown.

Related

How to determine button id in another view depending on a different view Rails

I'm making an app where some activities are listed in a table called Fakultety (polish language, sorry), and participants on in another table called Uczestnicy.
I have a submit form where you can submit yourself to an activity, but I'm stuck on passing values to a DB. Firstly, I don't know how to tell to the database on which activity you want to be assigned to (I tried to change the submit button id to an activity id and then passing it into a database but don't know how to do this id: "#{#fakultet.id}" not working) and later I want to count how many people are assigned to field participants in a database Fakultety, but I don't want to pass all the data, just ID of the users from table called Uczestnicy. How to do it? I mean just to pass the ids to another table, and how to tell the database on which activity I want to be assigned to?
This is my form view:
<h1>Zapisujesz sie na fakultet</h1>
<div class="form-group">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#participant, url: zapisy_path) do |f| %>
<p>Imię:</p>
<%= f.text_field :firstName, class: 'form-control' %>
<p>Nazwisko:</p>
<%= f.text_field :lastName, class: 'form-control' %>
<p>Grupa:</p>
<%= f.text_field :group, class: 'form-control' %>
<%= f.submit "Zapisz się", class: "btn btn-primary" id: "#{#fakultet.id}"%>
<% end %>
</div>
</div>
</div>
Does anybody understand me and can help me?
Rails provides form collection helpers that make it really easy to assign associations:
# I'm going to just do this in english
<%= form_for(#activity) do |f| %>
<%= f.collection_select(:participant_ids, Partipicant.all, :id, :name, prompt: true, multiple: :new) %>
# ...
<% end %>
Then whitelist the attribute as an array:
params.require(:activity).permit(:foo, :bar, participants_ids: [])
Thats all you actually need to assign childen to to a parent resource. This is done as a part of the normal create/update actions of the parent resource.
You don't actually need the form for the child records unless you actually want to be creating the record. In that case you can setup a nested resource or if you want to create/edit multiple nested records at the same time as the parent record you can use nested attributes.
First you should rename your models and tables, to English, it's a really bad pattern to use your language, in English it is easier to understand by other devs.
According to the problem, probably what you are looking for is hidden_field
<%= f.hidden_field :fakultet_id, :value => #fakultet.id %>
and if you want to query Fakultety that have user assigned, you can select Fakultety where participant_id is not nil
Fakultety.where.not(participant_id: nil)

Rails 5 collection_select: Showing multiple attributes in one column

I'm trying to make a collection select which shows two attributes from two different models.
I want to select an account. The account has a name and an owner. The owner is a model which also has the attribute name.
When using the collection select I want it to show: account.name + owner.name. This is currently the collection_select I have which only shows the account.name
<div class="field">
<%= f.label :to_account_id %>
<%= f.collection_select :to_account_id, Account.all, :id, :name %>
</div>
ex: A account has name Main account and the owner of the account is Stan, when selecting it should show Stan - Main account.
worked with:
<%= f.collection_select :to_account_id, Account.all.map{|a| ["#{a.owner.name} - #{a.name}", a.id] },:second,:first %>
Try following code
<%= f.collection_select :to_account_id, Account.all.map{|a| ["#{a.name} - #{a.owner.name}", a.id] } %>
class Classification < ApplicationRecord
def class_detail
"#{definition} #{description}"
end
end
<%= form.collection_select(:classification_id, Classification.order(:classification), :id, :class_detail, prompt: true)%>

Rails Multiple Input Field in Form to One Integer Attribute in Model

I am trying to allow a user to input two different things in two different drop down menus from the same form and it will store an integer into a review table.
I want the user to be able to select model_name in one drop down and manufacturer in another drop down. The result will store a bat_id integer into the form. (Telling you which bat the user is selecting)
I have seen a couple questions about date & time but they store the values directly in the model. I am trying to store an integer - bat_id so that the bat_id will directly link the review model to the bat model.
Examples I have found that are close:
How do ruby on rails multi parameter attributes really work (datetime_select)
Rails multiple fields to one model attribute
Using multiple input fields for one attribute
Rails Update Single Attribute with Multiple Fields
My form now:
<%= form_for(#review) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field" align= "center">
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
<h3>What do you like about this bat?</h3>
<%= f.text_area :pros, placeholder: "Enter what you like..." %>
<h3>What do you not like about this bat?</h3>
<%= f.text_area :cons, placeholder: "Enter what you don't like..." %></br>
</div>
<div align="center">
<%= f.submit "Add Review", class: "btn btn-large btn-info" %>
</div>
<% end %>
I am submitting to the review table and trying to submit both of these to the bat_id attribute.
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
In my bat model I have: has_many :reviews & In my reviews model I have: belongs_to :bat
UPDATE: Is it possible to use a hidden field with the combination of javascript and my two inputs to determine my one output bat_id?
Update I changed my dropdown code to what works so that I enter in manufacturer_id & bat_id when both are selected. However I still think there is a way to store one value in my review model. I am using javascript very similiar to this
From a UI perspective this seems broken... users will be able to associate any model year & name with any manufacturer, even if that manufacturer did not produce that model year & name.
Assuming you will introduce some javascript to handle that, from a rails perspective you will get undefined behavior with two :bat_id fields in the same form. I think you need this:
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all, :id, :model_year_and_name, include_blank: true %>
Alternatively you can just create one dropdown containing a composite field, like this:
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all.sort {|a, b| a.manufacturer_model_year_and_name <=> b.manufacturer_model_year_and_name}, :id, :manufacturer_model_year_and_name, include_blank: true %>
and then in your Bat model introduce something like this:
def manufacturer_model_year_and_name
"#{self.manufacturer.name}: #{self.model_year_and_name}"
end
As discussed in your other answer, you shouldn't need to store the manufacturer_id on your review model.
I would recommend creating a Manufacturer select that isn't accessed in your Review model, but is simply used to filter the list of bats on the form.
The best way to do this is probably to add some custom data attributes to the Bat select.
<%= collection_select :manufacturer, :manufacturer_id, Manufacturer.all, :id, :manufacturer %>
<%= f.select :bat_id, Bat.all.map{ |b| [b.model_year_and_name, b.id, {'data-manufacturer' => b.manufacturer_id}] } %>
Then use some javascript to filter the Bat select when the Manufacturer select is changed.
Unfortunately you cannot just set display: none to an option element to hide it. This does not hide the option in many browsers. So the best method is to use a bit of jQuery to clone the original select every time the manufacturer select is changed, and remove any option that isn't associated with the selected manufacturer. Like so:
// rename the original select and hide it
$('#bat_id').attr('id', 'bat_id_original').hide();
$('#manufacturer_id').on('change', function() {
$('#bat_id').remove(); // remove any bat_id selects
$bat = $('#bat_id_original')
.clone() // clone the original
.attr('id', 'bat_id') // change the ID to the proper id
.insertAfter('#bat_id_original') // place it
.show() // show it
.find(':not(option[data-manufacturer="' + $(this).val() + '"])')
.remove(); // find all options by other manufacturers and remove them
});
You might need to change a few things to get this to work in your installation, but you can view a static demo on jsFiddle here: http://jsfiddle.net/JL6M5/
You will probably need to reject the manufacturer_id field on form submit, avitevet already pointed out this answer which should help there: Rails: Ignoring non-existant attributes passed to create()

Rails link models in a form DRY

I am trying to link multiple rails models in my app. I am trying to let users create reviews on a product using a form. I am trying to use the rails DRY principle.
I first made a bat table with bat name, model year, and an image. Then I created a manufacturer table which only lists the names of the manufacturers of bats. My bats model belongs_to :manufacturer and my manufacturer model has_many :bats.
Instead of creating multiple tables using manufacturer, (listing the name of the manufacturer at least 3 times for each bat) how can I link my two models together?
My form is submitted to the review model. In the form I already have <%= f.collection_select :bat_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>, which lists all of the possible manufacturers in a drop down menu. HOWEVER, nothing is submitted to the :bat_id parameter in the review form when submitted.
--One guess is to have the manufacturer_id integer stored in the bat model as an integer under the column manufacturer_id(Note: already done this, but I don't know how to submit that in a form?)
--Another guess is to have the bat model inherit from the manufacturer model
Any help is greatly appreciated
My full form:
<%= form_for(#review) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field" align= "center">
<h3>Select bat</h3>
<%= f.collection_select :bat_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>What do you like about this bat?</h3>
<%= f.text_area :pros, placeholder: "Enter what you like..." %>
<h3>What do you not like about this bat?</h3>
<%= f.text_area :cons, placeholder: "Enter what you don't like..." %></br>
</div>
<div align="center">
<%= f.submit "Add Review", class: "btn btn-large btn-info" %>
</div>
<% end %>
according to the documentation
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
the params in the collection_select is supposed to be:
f.collection_select(:post, :author_id, Author.all, :id,
:name_with_initial, prompt: true)
where post is the Model,author_id is the attribute
or you could try using:
=f.select( :bat_id, options_from_collection_for_select(#manufacturers,"id","manufacturer",
f.object. bat_id),{})
and put the
#manufacturers = Manufacturer.all
inside the controller

Using form helpers to picking an object by name, but assigning the ID associated with that name

I have a Product model, an Order, and an OrderItem model.
An OrderItem object has a single Product (that is, it belongs to a product)
An Order has multiple order items.
When you want to add an OrderItem to your order, you fill out a form. One of the fields is to enter the product that will be assigned to this order item. The order model stores the product_id as a foreign key, and so Rails complains (as it should) when I write
<%= form_for #orderItem, url: {:action => :create} do |f| %>
<p>
<%= f.label :product_name %>
<%= f.text_field :product_name %>
</p>
<%= f.submit %>
<% end %>
What I have in mind is to to have someone use the form to create order items. When they choose which product to assign to this order item, they will pick a name from a list of product names (which are unique), and then when they submit the form, the controller retrieves the appropriate Product object and assigns its ID to the OrderItem.
How can I set up my form to accomplish this?
Sounds like you want to use nested attributes. They'll let you save information about both an OrderItem and a Product at the same time from a single form.
In your OrderItem, add the line accepts_nested_attributes_for :product.
Then in your form you'll want something like:
<%= form_for #orderItem, url: {:action => :create} do |f| %>
<p>
<%= fields_for :product do |fp| %>
<%= fp.label :product_name %>
<%= fp.text_field :product_name %>
<% end %>
</p>
<%= f.submit %>
<% end %>
(Note the use of fp.label instead of f.label within the fields_for block.
The above is from memory so there may be some mistakes (and you'll probably need to change things in your OrderItemsController too), but it should give you a starting point.
You might find this Railscast helpful too.

Resources