I have a string field in my databse
class CreateMHolidays < ActiveRecord::Migration
def change
create_table :m_holidays do |t|
t.string :open_schedule, :limit => 50
end
end
end
I am using time_select to get the value for open_schedule field.
<%= f.time_select :open_schedule, {minute_step: 01, include_blank: true,:default =>{:hour => '00', :minute => '00'},:ignore_date => true}, {:class => 'form-control'} %>
In my controller I try
#m_holidays = MHoliday.new(m_holiday_params)
#open_schedule_hrs = (params[:m_holidays]['open_schedule(4i)']).to_s
#open_schedule_mns = (params[:m_holidays]['open_schedule(5i)']).to_s
#m_holidays.open_schedule = #open_schedule_hrs + ':' + #open_schedule_mns
But when I try to save the record I am getting
ActiveRecord::MultiparameterAssignmentErrors (1 error(s) on assignment
of multiparameter attributes [error on assignment [3, 3] to
open_schedule (Missing Parameter - open_schedule(1))])
This is the first time I am using time_select and I must use it with a string field rather than :time. How to go about this? Any help much appreciated
You're getting the ActiveRecord::MultiparameterAssignmentErrors because of the mass parameter assignment on the line #m_holidays = MHoliday.new(m_holiday_params). This might be due to m_holiday_params containing parameters that your MHoliday model doesn't know what to do with.
Try filtering out everything related to the open_schedule input from m_holiday_params. If you have an m_holiday_params method like this:
def m_holiday_params
params.require(:m_holiday).permit('open_schedule(4i)', 'open_schedule(5i)', ...)
end
then omit the open_schedule parameters:
def m_holiday_params
params.require(:m_holiday).permit(...)
end
Then you can manually set up your open_schedule string, as you've already done.
Related
Rails 4.2.1
Ruby 2.1.5
I have the following helper method:
def parse_potential_followers(params)
t_id = TestSet.where(:test_name => params[:test_set][:test_name]).pluck(:id)
screen_names = get_screen_names
screen_names.each do |s|
potential_follower = PotentialFollower.new(
:screen_name => s,
:test_sets_id => t_id,
:status => 'new',
:slug => generate_slug([t_id, s])
)
logger.info("Test Set ID: #{t_id}")
potential_follower.save
end
end
The problem is that when I call this method, the test_sets_id is skipped when data is inserted in the table. The three other attributes are saved fine.
I verified through logger.info that t_id is valid.
All the attributes are defined in the potential_followers table.
I also have all the attributes in the potential_follower_params method in the potential_followers_controller.rb:
def potential_follower_params
params.require(:potential_follower).permit(:screen_name, :test_sets_id, :connections, :status,
:slug, :created_at, :updated_at)
end
What am I forgetting?
Answer:
t_id is an array (result of ActiveRecord query). If t_id is changed to t_id[0] when used in the hash, it will work fine
You get t_id by
t_id = TestSet.where(:test_name => params[:test_set][:test_name]).pluck(:id)
which is an array. Probably you should try to get a variable with integer type instead of array. If your test_sets_id is an integer, the value in array won't be saved.
My guess is the data type is different. Maybe you are trying to save string as an integer?
The first time I try to submit the form I get the error saying
"Price is not a valid number"
It's OK the second time I try to submit it (with the same valid data in :price field).
If I don't add validation in the model, then the form is submitted, but value of price is not saved.
What could be going on? Is there something special about .decimal field?
db schema:
t.decimal "price"
model
validates :price, numericality: { :greater_than => 0, :less_than_or_equal_to => 100000000 }
form view file
<%= f.number_field :price, class: "short red" %>
controller
def new
#product = Product.new
end
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render :new
end
end
private
def product_params
params.require(:product).permit(:name, :description, :image, :price, :user_id)
end
logs
Started POST "/products" for xxx.132 at 2014-10-15 22:56:51 +0000
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"abte/LtO0T/ZtSXQIuXVVjjUvwHw5jDUJ1yIKCOWRx2=",
"product"=>{"name"=>"", "description"=>"", "user_id"
=>"1"}, "commit"=>"Submit"}
Some things you can check:
The snippet from your form starts f.number_field. Check that you are using something like <%= form_for(#product) do |f| %> at the top of the form.
Try to create a product using the rails console.
In the rails console, try something like this:
> p = Product.new
> p.valid?
#=> TRUE or FALSE should appear
> p.errors.full_messages.to_sentence
# you should see a full list of all failed validations from your Product model
If these don't help, try pasting in the entire product_controller.rb and _form.html.erb files into your question, and I'll take a look again.
Try to change your migration to:
t.decimal :price, precision: 8, scale: 2 #for example
Then, change validation to:
validates :price, numericality: {greater_than_or_equal_to: 0.01, :less_than_or_equal_to => 100000000 }
In PostgreSQL next implementations behavior with :decimal columns:
PostgreSQL: :precision [1..infinity], :scale [0..infinity]. No
default.
I hope, this example from "Agile Web Development with Rails 4" help you to understand validation of decimal numbers:
it’s possible to enter a number such as 0.001 into this field. Because
the database stores just two digits after the decimal point, this
would end up being zero in the database, even though it would pass the
validation if we compared against zero. Checking that the number is at
least 1 cent ensures only correct values end up being stored.
This is the first time I'm using enums with rails 4 and I ran into some issues, have couple of dirty solutions in mind and wanted to check are there any more elegant solutions in place :
This is my table migration relevant part:
create_table :shippings do |t|
t.column :status, :integer, default: 0
end
My model:
class Shipping < ActiveRecord::Base
enum status: { initial_status: 0, frozen: 1, processed: 2 }
end
And I have this bit in my view (using simple form for) :
= f.input :status, :as => :select, :collection => Shipping.statuses, :required => true, :prompt => 'Please select', label: false
So in my controller:
def create
#shipping = Shipping.create!(shipping_params)
if #shipping.new_record?
return render 'new'
end
flash[:success] = 'Shipping saved successfully'
redirect_to home_path
end
private
def shipping_params
params.require(:shipping).permit(... :status)
end
So when I submit create form and the create action fire I get this validation error :
'1' is not a valid status
So I thought I knew that the issue was data type so I added this bit in the model :
before_validation :set_status_type
def set_status_type
self.status = status.to_i
end
But this didn't seem to do anything, how do I resolve this ? Has anyone had the similar experience?
You can find the solution here.
Basically, you need to pass the string ('initial_status', 'frozen' or 'processed'), not the integer. In other words, your form needs to look like this:
<select ...><option value="frozen">frozen</option>...</select>
You can achieve this by doing statuses.keys in your form. Also (I believe) you don't need the before_validation.
Optionally, you could add a validation like this:
validates_inclusion_of :status, in: Shipping.statuses.keys
However, I'm not sure that this validation makes sense, since trying to assign an invalid value to status raises an ArgumentError (see this).
My application has a model "Appointments" which have a start and end attribute both which are datetimes. I am trying to set the date and time parts separately from my form so I can use a separate date and time picker. I thought I should be able to do it like this. From what I ahve read rails should combine the two parts and then parse the combined field as a datetime like it usually would
The error I am getting:
2 error(s) on assignment of multiparameter attributes [error on assignment ["2013-09-16", "15:30"] to start (Missing Parameter - start(3)),error on assignment ["2013-09-16", "16:30"] to end (Missing Parameter - end(3))]
These are the request parameters:
{"utf8"=>"✓", "authenticity_token"=>"OtFaIqpHQFnnphmBmDAcannq5Q9GizwqvvwyJffG6Nk=", "appointment"=>{"patient_id"=>"1", "provider_id"=>"1", "start(1s)"=>"2013-09-16", "start(2s)"=>"15:30", "end(1s)"=>"2013-09-16", "end(2s)"=>"16:30", "status"=>"Confirmed"}, "commit"=>"Create Appointment", "action"=>"create", "controller"=>"appointments"}
My Model
class Appointment < ActiveRecord::Base
belongs_to :patient
belongs_to :practice
belongs_to :provider
validates_associated :patient, :practice, :provider
end
And the relevant part of the view: (its a simple form)
<%= f.input :"start(1s)", :as => :string, :input_html => { :class => 'date_time_picker' , :value => Date.parse(params[:start]) }%>
<%= f.input :"start(2s)", :as => :string, :input_html => { :class => 'date_time_picker' , :value => Time.parse(params[:start]).strftime('%R') }%>
<%= f.input :"end(1s)", :as => :string, :input_html => { :class => 'date_time_picker' , :value => Date.parse(params[:end]) }%>
<%= f.input :"end(2s)", :as => :string, :input_html => { :class => 'date_time_picker' , :value => Time.parse(params[:end]).strftime('%R') }%>
UPDATE:
THis is now how my model looks like, Ive been trying to do getter/setter methods but I am stuck because start-dat, start_time etc are nil in the model and the parameters aren't sent through
class Appointment < ActiveRecord::Base
belongs_to :patient
belongs_to :practice
belongs_to :provider
validates_associated :patient, :practice, :provider
before_validation :make_start, :make_end
############ Getter Methods for start/end date/time
def start_time
return start.strftime("%X") if start
end
def end_time
return self.end.strftime("%X") if self.end
end
def start_date
return start.strftime("%x") if start
end
def end_date
return self.end.strftime("%x") if self.end
end
def start_time=(time)
end
def end_time=(time)
end
def start_date=(date)
end
def end_date=(date)
end
def make_start
if defined?(start_date)
self.start = DateTime.parse( self.start_date + " " + self.start_time)
end
end
def make_end
if defined?(end_date)
self.start = DateTime.parse( end_date + " " + end_time)
end
end
end
Are you trying to emulate #date_select ? If yes, see second part of answer.
Date database typecast
If you want to assign a DateTime to database, it has to be a DateTime object. Here you use an array of strings, ["2013-09-16", "15:30"].
You can easily compute a datetime from those strings using regexps :
/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/ =~ params[ 'start(1s)' ]
/(?<hours>\d+):(?<minutes>\d+)/ =~ params[ 'start(2s)' ]
datetime = DateTime.new( year.to_i, month.to_i, day.to_i, hours.to_i, minutes.to_i )
This will store year, month, day, hours and minutes in local variables and create a new datatime based on it, which you can then assign to your model.
Yet, databases can't store ruby DateTime instances as is, so behind the hood, a conversion is made by rails when saving a date or datetime field to convert it as string. The method used is #to_s(:db), which gives, for example :
DateTime.now.to_s(:db) # => "2013-09-17 09:41:04"
Time.now.to_date.to_s(:db) # => "2013-09-17"
So you could theoretically simply join your strings to have proper date representation, but that wouldn't be a good idea, because :
that's implementation details, nothing say this date format won't change in next rails version
if you try to use the datetime after assigning it and before saving (like, in a before_save), it will be a string and not a datetime
Using active_record datetime helpers
As this would be a pain to do that all the time, rails has helpers to create and use datetime form inputs.
FormBuilder#datetime_select will take only the attribute you want and build all needed inputs :
<%= f.datetime_select :start %>
This will actually create 5 inputs, named respectively "start(1i)" (year), "start(2i)" (month), "start(3i)" (day), "start(4i)" (hours) and "start(5i)" (minutes).
If it feels familiar, it's because it's the exact data we retrieved for building a datetime in first part of this answer. When you assign a hash to a datatime field with those exact keys, it will build a datetime object using their values, like we did in first part.
The problem in your own code is that you've just provided "start(1i)" and "start(2i)". Rails doesn't understand, since you only passed it the year and month, a lot less than what is required to compute a datetime.
See How do ruby on rails multi parameter attributes *really* work (datetime_select)
According to this question, the multiparameter attribute method works for Date but not DateTime objects. In the case of a Date, you would pass year, month and day as separate values, hence the Missing Parameter - start(3), as the expected third parameter is not there.
DateTime, however, requires at least five params for instantiation DateTime.new(2013, 09, 16, 15, 30), so you cannot rely on the automated parsing in your case. You would have to split your params first and in that case, you could easily parse it yourself before saving the object using a before_filter or similar methods.
See the constructor:
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/DateTime.html#method-c-new
and the multiparam description:
http://apidock.com/rails/ActiveRecord/AttributeAssignment/assign_multiparameter_attributes
I have a class Sample
Sample.class returns
(id :integer, name :String, date :date)
and A hash has all the given attributes as its keys.
Then how can I initialize a variable of Sample without assigning each attribute independently.
Something like
Sample x = Sample.new
x.(attr) = Hash[attr]
How can I iterate through the attributes, the problem is Hash contains keys which are not part of the class attributes too
class Sample
attr_accessor :id, :name, :date
end
h = {:id => 1, :name => 'foo', :date => 'today', :extra1 => '', :extra2 => ''}
init_hash = h.select{|k,v| Sample.method_defined? "#{k}=" }
# This will work
s = Sample.new
init_hash.each{|k,v| s.send("#{k}=", v)}
# This may work if constructor takes a hash of attributes
s = Sample.new(init_hash)
Take a look at this article on Object initialization. You want an initialize method.
EDIT You might also take a look at this SO post on setting instance variables, which I think is exactly what you're trying to do.
Try this:
class A
attr_accessor :x, :y, :z
end
a = A.new
my_hash = {:x => 1, :y => 2, :z => 3, :nono => 5}
If you do not have the list of attributes that can be assigned from the hash, you can do this:
my_attributes = (a.methods & my_hash.keys)
Use a.instance_variable_set(:#x = 1) syntax to assign values:
my_attributes.each do |attr|
a.instance_variable_set("##{attr.to_s}".to_sym, my_hash[attr])
end
Note(Thanks to Abe): This assumes that either all attributes to be updated have getters and setters, or that any attribute which has getter only, does not have a key in my_hash.
Good luck!