Rails4 Strong parameters without database column - ruby-on-rails

I have a problem with the mass-assignment and strong parameters. In my model I have several attributes, that are represented through a column in my mysql database. I also have a field, that isn't. I just need it to calculate a value. I don't want to store it in my database.
In my controller I defined the strong parameters:
def timeslots_params
params.require(:timeslot).permit(:employee_id, :dienstplan_id, :start_date, :start_time)
end
But when I'm trying to access the start_time attribute in my controller, I'm getting the
undefined method `start_time' for #<Timeslot:0x007fff2d5d8070> error.
The other defined parameters with db columns are present and filled with values. Do I missunderstand the strong parameters and have to define something else?
EDIT: Here is the code, where I call the timeslots_params:
def create
#e = Timeslot.new(timeslots_params)
#e.start_date = #e.start_date.to_s + " " + #e.start_time.to_s
if #e.save
current_user.timeslots << #e
respond_to do |format|
format.json { render :json => #e }
end
end
end

In your Model provide access to the start_time field:
attr_accessor :start_time
If you only need read access:
attr_reader :start_time

Please, permit only the params you expect your user send with data. If start_time is not user data for update your db, use this way:
params.require(:timeslot).permit(:employee_id, :dienstplan_id, :start_date)
Strong parameters prevents save data that user sends and you don't want he/she update.
If you use :start_time, it must be defined at your model.
Ops, I've seen your update:
#e.start_date = #e.start_date.to_s + " " + #e.start_time.to_s
If you send :start_time to an Timeslot instance them start_time must be a method of Timeslot model. Defined by rails if it is a db field, defined with attr_accesor or att_reader or defined by a def key on your source model.
If not #e.start_time trigger undefined method 'start_time'.
edited again:
Now, start_time is a model variable. Make sure it is send to the form in the same way that you do with the fields. By default we use an f <%= f.text_field :the_field %>. Just don't forget the f.
Now you must permit this field again, if you follow my first advice.

While Alireza's answer couldwork, you might also try defining the setter method yourself, then you can play with the variable before save.
def start_time=(time)
# use variable time to do what you want with the model
end

Related

How would one edit a params/parameters attribute before validation or save in the Model?

Short question...
How do I edit an attribute passed to the model before validation or save?
Long question...
In my app I have a unique number generator so that entries can be indexed on an randomly generated unique number, so that users can not find that entry (In other words an alternative to using ID, which a user can randomly guess).
So in my model I have...
def self.generate_photo_number
record = Object.new
while record
random = rand(9223372036854775806)
record = Photo.first(:conditions => ["unique_photo_no = ?", random])
end
return random
end
And in my forms I have the input...
<%= hidden_field(:photo, :unique_photo_no, value: Photo.generate_photo_number) %>
i.e.
<input id="photo_unique_photo_no" name="photo[unique_photo_no]" type="hidden" value="233720368547758" />
Thing is, if the user submits the form, presses the back button, changes an option and submits again, the rails app will find an entry with that same unique_photo_no and cause a save error.
I could create a JavaScript function that generates a number on clicking submit (which I have done for pages outside the Rails app environment), but the beauty with the generate_photo_number function is that it checks to make sure it does not already exist - whereas the JavaScript does not.
How can I generate the number before validation and save it in the unique_photo_no attribute?
This is what I have put so far in my Photo model...
before_validation :generate_photo_number
validates_uniqueness_of :unique_photo_no,
:message => " - is already in use."
def self.generate_photo_number
record = Object.new
while record
random = rand(9223372036854775806)
record = Photo.first(:conditions => ["unique_photo_no = ?", random])
end
#photo.unique_photo_no = random
end
You will not be able to edit the attribute once it has passed to the Model.
Instead, edit the attribute before it is passed to the Model from the Controller.
For this question example, do the following...
Remove the form input field for unique_photo_no, as it is no longer needed
Add this to the 'create' action only of the Photos controller, beneath the #photo instance variable. Do not add it to the 'update' action, otherwise a new number will be generated each time it is updated.
def create
#photo = Photo.new(params[:photo])
#photo.unique_photo_no = Photo.generate_photo_number
.
.
.
end
Photo(being the Model).generate_photo_number(being the function called)
Rails callbacks NOT a CLASS method so you need to remove self in your model than i hope it will work.
if you want to validate the data while creating a new record mean you can use before_create callbacks
for your before validation callback should be in
before_validation :generate_photo_number
validates_uniqueness_of :unique_photo_no,
:message => " - is already in use."
def generate_photo_number
record = Object.new
while record
random = rand(9223372036854775806)
record = Photo.first(:conditions => ["unique_photo_no = ?", random])
end
self.unique_photo_no = random
end
it will automatically call when your hitting save or update.

Using Rails model to update to change one boolean value based on another

I'm trying to make a pet rails app. My pet model includes two boolean values, hungry and feed_me. Right now, hungry and feed_me can both be set in the view, but I'm trying to set up the model so that if feed_me is true, hungry will automatically be changed to false. No matter what I do, however, feed_me never resets hungry. This is what I have in the model now:
attr_accessor :feed_me
before_save :feed
def feed
#feed_me = Creature.find(params[:feed_me])
#hungry=Creature.find(params[:hungry])
if #feed_me==true
#hungry=false
end
end
I'm new to Rails, but my understanding is that model should have access to the params hash, so I'm confused about why I can't use it to reset values.
You're on the right track using model callbacks, however models don't have access to the param hash - its available to controllers.
The model already knows the value of it's own attributes, so you don't need to get them from params. The controller I imagine is updating feed_me.
Also you shouldn't need to declare feed_me as an attr_accessor assuming it is backed by a database column.
You can change before_save to:
def feed
if self.feed_me
self.hungry = false
end
end
In your controller, I imagine you'd do something like:
def update
pet = Pet.find(params[:id])
pet.feed_me = params[:feed_me]
if pet.save
redirect_to pet_path(pet)
else
flash[:notice] = 'Error saving pet'
render :edit
end
end

Bind paramerts in Object if those present there

I am receiving an API call at my server with parameters
first_name , :last_name , :age
etc
I want to bind those params to my object against which user is having attribute with same name , like i want to have these in user[first_name] , user[:last_name]
so that I can just put the complete user object into database in following way ,
User.new(params[:user]) or User.new(some_hash)
I dont want to use the following ,
User.new(:first_name=>params[:first_name],:last_name=>params[:last_name])
thanks in advance for you help :)
Something like this may work:
user = User.new
params.each do |key,value|
user[key] = value if user.attribute_names.include?(key.to_s)
end
Note, however, that you should protect sensitive attributes of your User model with attr_protected or attr_accessible in this case.
Writing that functionality into User.initialize can take care of this:
def initialize(args={})
args.each_with_key do |key,val|
instance_variable_set("##{key}", val)
end
end
This of course has no validation and does not protect your object from bad data. For example, if you want to make sure only valid accessible attributes are being set, add if respond_to? key to end end of line 3.

update_attributes field tweaks

So I've got an edit page that has butt-load of editable fields on it...simple update...
#patient.update_attributes(params[:patient])...everything's great, except....
I've got one field out of these 20 that I need to tweak a little before it's ready for the db and it would seem I either need to do
two trips
#patient.update_attributes(params[:patient])
#patient.update_attribute( :field=>'blah')
or set them all individually
patient.update_attributes(:field1=>'asdf', :field2=>'sdfg',:field3=>'dfgh', etc...)
Am I missing a way to do this is one swoop?
What's the attribute you need to tweak? There's two ways to do this:
Either massage the params before you send them to the update_attribute method:
I'm just giving an example here if you wanted to underscore one of the values:
params[:patient][:my_tweak_attribute].gsub!(" ", "_")
#patient.update_attributes(params[:patient])
Then there's the preferred way of doing your tweaking in a before_save or before_update callback in your model:
class Patient < ActiveRecord::Base
before_update :fix_my_tweak_attribute, :if => :my_tweak_attribute_changed?
protected
def fix_my_tweak_attribute
self.my_tweak_attribute.gsub!(" ", "_")
end
end
This keeps your controller clean of code that it probably doesn't really need.
If you just need to add a new param that didn't get sent by the form you can do it in the controller like this:
params[:patient][:updated_by_id] = current_user.id
#patient.update_attributes(params[:patient])
Assuming current_user is defined for you somewhere (again, just an example)
You can create a virtual attribute for that field. Say the field is :name. You create a function in your Patient model like :
def name
self[:name] = self[:name] * 2
end
And of course, you do your things inside that function :) Instaed of self[:name], you can also use read_attribute(:name).

Accessing model properties in Rails

So basically I have a controller. something like this
def show
#user = User.find[:params[id]]
#code to show in a view
end
User has properties such as name, address, gender etc. How can I access these properties in the model? Can I overload the model accesser for name for example and replace it with my own value or concatenate something to it. Like in the show.html.erb view for this method I might want to concatenate the user's name with 'Mr.' or 'Mrs.' depending upon the gender? How is it possible?
I would hesitate to override the attributes, and instead add to the model like this:
def titled_name
"#{title} #{name}"
end
However, you can access the fields directly like this:
def name
"#{title} #{self[:name]}"
end
You can create virtual attributes within your model to represent these structures.
There is a railscast on this very subject but in summary you can do something like this in your model
def full_name
[first_name, last_name].join(' ')
end
def full_name=(name)
split = name.split(' ', 2)
self.first_name = split.first
self.last_name = split.last
end
If you wish to explicitly change the value of an attribute when reading or writing then you can use the read_attribute or write_attribute methods. (Although I believe that these may be deprecated).
These work by replacing the accessor method of the attribute with your own. As an example, a branch identifier field can be entered as either xxxxxx or xx-xx-xx. So you can change your branch_identifier= method to remove the hyphens when the data is stored in the database. This can be achieved like so
def branch_identifier=(value)
write_attribute(:branch_identifier, value.gsub(/-/, '')) unless value.blank?
end
If you are accessing data stored directly in the database you can do this in you view:
<%= #user.firstname %>
<%= #user.gender %>
etc.
If you need to build custom representations of the data, then you will either need to create helpers, or extend the model (as above).
I tend to use helper methods added to the model for things like that:
def formatted_name
"#{title} #{first_name} #{last_name}"
end
(Edit previous post. Looked back at my code and realized helpers are supposed to be for presentation-related (mark-up) stuff only.)
(Edit again to remove left-over parameter... Geez, not enough coffee this morning.)
(Edit again to replace $ with #... Perhaps I should just remove this one huh?)
You can easily overload the attributes as you suggest.
i.e. if name is a field in the users database table, you can do:
def name
"#{title} #{read_attribute[:name]}"
end
The read_attribute function will return the database column value for the field.
Caveat: I am not sure this is a good idea. If you want a method that displays model data in a modified way, I would be tempted not to overload the default methods, and call them something different - this will avoid a certain level of obfuscation.
Documentation here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html (under 'Overwriting default accessors')
in http://api.rubyonrails.org/classes/ActionController/Base.html
search for
Overwriting default accessors

Resources