I have a student model with fields :name, :address.
I want to create 2 different fields :country, :city, that, when filled in and submitted, would concatenate into the address db field.
How can it be done? Must I define something in student.rb? Or just some form view?
my student/_form.haml:
= simple_form_for #student do |f|
= f.input :name
= f.input :country #not in db
= f.input :city #not in db
= f.button :submit
(address must equal country + " " + city)
Note: I do not want to create separate db fields for country & city.
You can do it in following ways
make address in model --> this is the best way
make address in controller --> this is good way
way no 1
in model
attr_accessor :country
attr_accessor :city
def before_save
self.address = #country + " " + #city
end
in controller
def save
student = Student.new()
student.country = params[:country]
student.city = params[:city]
student.save
end
way no 2
For example model student, controller Student
in controller
def save
student = Student.new
student.address = params[:country] + " " + params[:city]
student.save
end
I hope it helps.
Related
I have a model A that can have up to 10 associated models B in a one-to-many relationship. These nested models have just a string attribute representing a word.
I want to display a form to create/edit the parent model and all the nested children, displaying fields for the 10 possible models. Then, if I only fill up two of them, two models will be created.
Finally, when editing model A I need to display 10 fields, two of them filled up with the model B associated with A data, and the rest blank ready to fill.
Tried fields_forwith an array, but it only displays fields for the already existing model B instances.
View:
= form_for #a, remote: true do |f|
= f.text_field :title, placeholder: true
= f.fields_for :bs, #a.bs do |ff|
/ Here, for the edit action, N text fields appear, being N equals to #soup.soup_words.size
/ and I need to display 10 fields everytime, because a Soup can have up to 10 SoupWord
/ For the new action, it should display 10 empty text fields.
/ Finally, if you fill three of the 10 fields,
/ model A should have only 3 instances of model B associated. i.e if there were 4 filled and
/ I set one of them blank, the model B instance should be destroyed.
= ff.text_field :word, placeholder: true
= f.submit
Controller:
class Bs < ApplicationController
def edit
respond_to :js
#soup = Soup.find params[:id]
end
def update
respond_to :js
puts params
end
end
Update
Create and edit actions now work, just put a reject_if parameter in model A,
accepts_nested_attributes_for :bs, reject_if: proc { |attrs| attrs[:word].blank? }
and set the build on the controller.
def new
respond_to :js
#a = A.new
10.times { #a.bs.build }
end
def edit
respond_to :js
#a = Soup.find params[:id]
#a.bs.size.upto(9) do |sw|
#a.bs.build
end
end
Now I need to destroy instances of model B if I set them blank in the edit action.
Normally you would delete nested records by using the allow_destroy: true option and by passing the _destroy param:
class Soup
accepts_nested_attributes_for :soup_words,
reject_if: proc { |attrs| attrs[:word].blank? },
allow_destroy: true
end
To get the behavior you want you can use javascript with a hidden input:
= form_for #soup, remote: true do |f|
= f.text_field :title, placeholder: true
= f.fields_for :soup_words, #soup.soup_words do |ff|
= ff.text_field :word, class: 'soup_word', placeholder: true
= ff.hidden_input :_destroy
= f.submit
$(document).on('change', '.soup_word', function(){
var $obj = $(this);
if (!this.value || !this.value.length) {
// set soup_word to be destroyed
$obj.siblings('input[name~=_destroy]').val('1');
}
$obj.fadeOut(50);
});
Make sure you have whitelisted the _destroy and id params.
def update_params
params.require(:soup).permit(:soup_words_attributes: [:word, :id, :_destroy])
end
I'm trying to implement a persitent model Setting storage in Rails, using the Active Record. I've already saw other gems like ledermann/rails-settings, but I don't want other dependency, because I'll use it only for one model and need the ability to customize it.
I've created 3 models, "Company", "Setting", "CompanySetting". For the association, I done the follow:
company.rb
has_many :company_settings
setting.rb
has_many :company_settings
has_many :company, through: :company_settings
company_setting.rb
belongs_to :company
belongs_to :setting
But I've a problem, for example, I seed the Settings table with N settings, and I need to have these Settings built when I try to access the Company settings, since they don't have an CompanySetting entry for that Setting.
My attempt was the follow:
company.rb
has_many :company_settings
accepts_nested_attributes_for :company_settings
def load_company_settings
Setting.all.collect { |setting|
company_settings.find_by( setting: setting ) || company_settings.build( { setting: setting, value: '' } )
}
end
And then, in my form (using Simple Form):
= f.simple_fields_for :company_settings, #company.load_company_settings do |s|
= s.input :value
It renders the correctly number of fields (the N fields in my Setting table), and return they values if exist, otherwise, returns an empty string as value. But when I do a POST, it doesn't saves.
I believe that I'm doing the right thing in Rails 4 Strong Params, so, my companies_controller look like that:
class Company::CompaniesController < Company::BaseController
def show
#company = current_company
end
def edit
#company = current_company
end
def update
#company = current_company
if #company.update(company_params)
redirect_to company_path
else
render 'edit'
end
end
private
def company_params
params.require(:company).permit(:name, company_settings_attributes: [:id, :value, :setting])
end
end
Table Structure - Company:
id
Table Structure - Setting:
title (value to show to user)
key (value used in application)
Table Structure - CompanySetting:
company_id
setting_id
value
Thanks (:
I fixed that, with these steps:
.1 Customize the Model adding the methods for return the list of all possible settings from Setting model, and build new ones with CompanySetting. You need to create an assign method in the class, because when the Strong Params try to save, it will try to find this method.
def settings
Setting.all.collect { |setting|
company_settings.find_by( setting: setting ) || company_settings.build( { setting: setting, value: '' } )
}
end
def set_setting(key, value)
company_settings.find_or_create_by( setting: Setting.find_by(key: key) ).update(value: value)
end
def settings=(attributes)
attributes.map { |key, value|
set_setting(key, value)
}
end
asd
.2 Update the Form to use this new method (Here I added two types of fields, booleans and not booleans. This is based on is_boolean property in Setting
= f.simple_fields_for :settings do |s|
- for setting in #company.settings
.form-group
- if setting.setting.is_boolean
.checkbox
%label
= s.input_field setting.setting.key.to_sym, as: :boolean, boolean_style: :inline, checked: setting.value == "1"
= setting.setting.title
%span.help-block= setting.setting.description
- else
= s.label setting.setting.key.to_sym, setting.setting.title, class: 'control-label'
= s.input_field setting.setting.key.to_sym, class: 'form-control', value: setting.value
%span.help-block= setting.setting.description
.3 Fix your Strong Params
def company_params
params.require(:company).permit(:name,
settings: [
:setting_key_1,
:setting_key_2,
...,
:my_other_n_setting
]
)
end
Done.
here is my code:
Perk not save on multiple select,when multiple true/false. perk save and habtm working.
class Perk < ActiveRecord::Base
has_and_belongs_to_many :companies
end
class Company < ActiveRecord::Base
has_and_belongs_to_many :perks
end
view perk/new.html.erb
<%= select_tag "company_id", options_from_collection_for_select(Company.all, 'id', 'name',#perk.companies.map{ |j| j.id }), :multiple => true %>
<%= f.text_field :name %>
Controller's code:
def new
#perk = Perk.new
respond_with(#perk)
end
def create
#perk = Perk.new(perk_params)
#companies = Company.where(:id => params[:company_id])
#perk << #companies
respond_with(#perk)
end
Your select_tag should return an array of company_ids:
<%= select_tag "company_ids[]", options_from_collection_for_select(Company.all, 'id', 'name',#perk.companies.map{ |j| j.id }), :multiple => true %>
http://apidock.com/rails/ActionView/Helpers/FormTagHelper/select_tag#691-sending-an-array-of-multiple-options
Then, in your controller, reference the company_ids param:
#companies = Company.where(:id => params[:company_ids])
(I assume that you've intentionally left out the #perk.save call in your create action... Otherwise, that should be included as well. Model.new doesn't store the record.)
It sounds like you may not have included company_id in the perk_params method in your controller. Rails four uses strong pramas this means you need to state the params you are allowing to be set.However it is difficult to say for sure without seeing more of the code.
In your controller you should see a method like this (there may be more options that just :name):
def perk_params
params.require(:perk).permit(:name)
end
You should try adding :company_id to it so it looks something like this:
def perk_params
params.require(:perk).permit(:name, :company_id)
end
if there are other params int your method leave them in and just added :company_id
EDIT to original answer
The above will only work on a one-to-many or one-to-one because you are using has_and_belongs_to_many you will need to add companies: [] to the end of your params list like this
def perk_params
params.require(:perk).permit(:name, companies: [] )
end
or like this
def perk_params
params.require(:perk).permit(:name, companies_ids: [] )
end
See these links for more details:
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
What is the purpose of defining methods inside a model like the example here? What does this get me? I was under the impression that only the fields of a model are defined in the model.
class Bean
include Mongoid::Document
field :name, type: String
field :roast, type: String
field :origin, type: String
field :quantity, type: Float
has_many :pairings
# has_many :pastries
def pastries
Pastry.find pastry_ids
end
#accepts_nested_attributes_for :pastries
def pastry_ids
pastry_ids_array = []
self.pairings.each do |one_pairing|
if one_pairing.pastry_id
pastry_ids_array.push one_pairing.pastry_id
end
end
pastry_ids_array
end
def pastry_ids=(list)
self.pairings.destroy
list.each do |pastry_id|
self.pairings.create(pastry_id: pastry_id)
end
end
# some way of showing a list
def pastry_list
pastries_string = ""
pastries.each do |one_pastry|
pastries_string += ", " + one_pastry.name
end
pastries_string.slice(2,pastries_string.length - 1)
pastries_string
end
end
I don't know if you know enough ruby but let's say you don't. This is a basic Class question? Defining methods on a model it's like having an helper. Let's say that you have
class CanadianPopulation
attr_accessor :population, :number_of_french_speaker, :number_of_english_speaker
def initialize(a,b,c)
#population = a
#number_of_french_speaker = b
#number_of_english_speaker = c
end
def total_people_that_have_a_different_mother_tongue
#Canadian who speak english or french but have a different mother tongue
self.population - (self.number_of_french_speaker + self.number_of_english_speaker)
end
end
census_2014 = CanadianPopulation.new(34_000_000, 4_000_000, 12_000_000)
let's say that you didn't have the method total_people_that_have_a_different_mother_tonguehow will you do to retrieve the total number of Canadians that have a different mother tongue? you will do the caculation yourself like for a view
<p>Canadian who speak english or french but have a different mother tongue
<br>
<%= #census = #census.population - (#census.number_of_english_speaker + #census.number_of_french_speaker) %>
</p>
Your view or your controller shouldn't do much logic (calculations) so that's one of the reason why you have a method inside the model (or class) it should be like this
<p>Canadian who speak english or french but have a different mother tongue
<br>
<%= #census.total_people_that_have_a_different_mother_tongue %>
</p>
For the second part of your question what does those methods do. rails c -s on your terminal than call or create a new instance model Bean and check to see what it does (the output/results)
Bean.first
b = _
b.pastries
b.pastry_ids
b.pastry_list
edit: #paul-richer recommends to maintain a thin controller
i have a opponents model, and a team model, i want to be able to create opponents on the fly, and have them assigned to a team id
at present within my model i have, which is creating the opponent but with a null team_id
def opponent_name
opponent.try(:name)
end
def opponent_name=(name)
self.opponent = Opponent.find_or_create_by_name_and_team_id(name,self.team_id) if name.present?
end
and in my view i am calling this method with the following
.row
.columns.large-2
= f.label :opponent_name, :class =>'left inline'
.columns.large-4
= f.text_field :opponent_name, data: {autocomplete_source: Opponent.order(:name).map(&:name)}
Shouldn't it be:
def opponent_name=(name)
self.opponent = Opponent.find_or_create_by_name_and_team_id(name,self.id) if name.present?
end
if this is a function in the Team model?