I need to pass a value to attribute in a model from a different controller with no direct relation between them. In the below example I need to update farming_year in the Field Model from the Planting controller.
The Field model:
class Field < ApplicationRecord
has_many :crops
attr_accessor :farming_year
def getting_crops
#crops_list = Crop.select('crops.name').where(field_id: self.id, year: self.get_farming_year) # doesn't get the farming_year
end
def get_farming_year
#farming_year # passing the value directly will work #farming_year=2015!!
end
def farming_year=(val)
#farming_year = val # passing the value directly won't work #farming_year=2015!!
end
end
In the Planting controller:
def new
#field = Field.new
#field.farming_year = session[:working_year]
#field.save
flash.now[:success] = #field.get_farming_year # it works and gives the correct year
end
when I changed the #farming_year in the get_farming_year method to #farming_year=2016, then the code will work and will give the correct Crops records. the flash message in the code above without any change will give the correct year from the model. I think my main issue is passing the farming year from get_farming_year method to getting_crops method.
Hint: the framing year is belong to the Crop not to the Field, so I don't need to add it to the Field table.
Any ideas how to achieve that?
Your code has a number of issues.
attr_accessor
Why are you using an attr_accessor? You should store the value on a model attribute, in the database. If your Field model doesn't already have a farming_year attribute, create a migration to add it to the database by running these commands:
$ rails g migration AddFarmingYearToField farming_year:integer
$ rails db:migrate
If you're running Rails <= 4, use rake db:migrate instead of the second command.
Doing this means you don't need to use attr_accessor, or define getters and setters.
PlantingController#new
This method isn't working for you because you haven't defined the correct methods, and you're not saving the instance.
In your Field model, you've defined a farming_year method, but you haven't defined a farming_year= method, which is what the setter should be. Change your farming_year method definition to farming_year=. Alternatively, use the method I described in 1., then you won't have to.
Make sure you're saving the model object once you're done with it - call Field#save, which returns truthy on success and falsy on failure; or call Field#save!, which returns truthy on success and raises an exception on failure.
The main issue with my code was using attr_accessor which I didn't need it, so, I've replaced "attr_accessor :farming_year" with a class variable "##work_year =''", and updated the getter and setter method as in the below code
The Field model:
class Field < ApplicationRecord
has_many :crops
attr_accessor :farming_year
##work_year =''
def getting_crops
#crops_list = Crop.select('crops.name').where(field_id: self.id, year: farming_year) #now this can request the getter method and get the year
end
def farming_year # getter method
##work_year ||= ''
end
def farming_year=(val) #setter method
##work_year = val
end
end
In the Planting controller:
def new
#field = Field.new
#field.farming_year = session[:working_year]
##field.save NO need for this line
flash.now[:success] = #field.farming_year
end
Thank you all for your kind support:)
Related
I have a method like this:
class Foo < ActiveRecord::Base
def load_data(data)
self.foo = data[:foo] if data.has_key?(:foo)
self.bar = data[:bar] if data.has_key?(:bar)
self.moo = data[:moo] if data.has_key?(:moo)
self.save
end
end
I want to write the method like this:
[:foo, :bar, :moo].each do |sym|
# need some trick here
self.sym = data[sym] if data.has_key?(sym)
end
Of course this method doesn't work, how can I assign a value to a Model column by using a symbol?
vee's answer is correct for the general case, but since this is Rails and ActiveRecord, you can take some nice shortcuts:
def load_data(data)
update_attributes data.slice(:foo, :bar:, :moo)
end
This works because data.slice filters your data hash to just the given keys, and then update_attributes will set those values in your model and invoke #save. When the keys aren't present, they aren't written, so you don't need to check and assign each key separately.
If you don't care about filtering the inbound data and simply assigning the keys given to the model, then just update_attributes data and you're done.
You can use send:
[:foo, :bar, :moo].each do |sym|
# need some trick here
send "#{sym}=", data[sym] if data.has_key?(sym)
end
I have a user model => #user
I want to add new attribute current_time to #user for temporary use.
Don't want to do migration to add a column (just for temporary use):
#user.current_time = Time.now
Is there any way to achieve this?
NoMethodError (undefined method `current_time=' for #<User:0x007fd6991e1050>):
app/controllers/carts_controller.rb:47:in `block in search_user'
app/controllers/carts_controller.rb:45:in `search_user'
attr_accessor will set up a reader and writer for the instance variable:
class Foo
attr_accessor :current_time
end
foo = Foo.new
foo.current_time = Time.now # Writes value
foo.current_time # Reads value
You might also be interested in attr_reader and attr_writer.
Try to define few methods in User.model:
def current_time= (time)
#current_time = time
end
def current_time
#current_time
end
UPD according to precisely right comment from kristinalim
Note, that attr_accessible, being part of framework, was deprecated in Rails 4. Now strong params are used instead. At the same time getter/setter attr_accessor is part of core Ruby and works as usual.
The difference between attr_accessible and attr_accessor is in very well explained in this post
I have a field in my model for date created, this is not passed from the form and is currently set in the create method of my controller.
Should this be in my model instead in some sort of initializer method? If so what would that method look like?
I have other fields I want to set as a default each a record is created so I'm trying to find out where is the accepted standard place to put these. I'm starting to think it should be the model as if the model was ever called outside the controller it wouldn't have all this logic.
I generally create builders and never use directly the standard Rails method create.
The point is to gather all the logic in one place with particular cases etc...
Basically in controllers I end up calling the builders this way:
#my_model_instance = MyModelBuilder.new(current_user, params[:my_model]).build
#my_model_instance = MyModelBuilder.new(current_user, params[:my_model]).create
All my builders live in /app/builders
Here is a very basic example:
class MyModelBuilder
attr_accessor :params, :user, :my_model
# consider using a Struct if you keep a very basic initializer
def initialize(user, params)
self.user = user
self.params = params
end
def build
my_model
end
def create
my_model.tap{|m| m.save }
end
def my_model
#my_model ||= MyModel.new(default_values.merge(params))
end
def default_values
{
foo: 'bar'
}
end
end
Rails already manages the date of creation and update of your records.
If your model has a created_at field or an updated_at field they will be filled with the time of creation and update of your model.
You can generate those fields easily in a migration, for instance :
create_table :hello do
t.timestamps
end
Now, for default values, you can fill them in the initialize method of the model :
def initialize(*args)
self.field = default_value
super(*args)
end
I am using Ruby on Rails 3.2.2 and I would like to know if the following is a "proper"/"correct"/"sure" way to override a setter method for a my class attribute.
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self[:attribute_name] = value
end
The above code seems to work as expected. However, I would like to know if, by using the above code, in future I will have problems or, at least, what problems "should I expect"/"could happen" with Ruby on Rails. If that isn't the right way to override a setter method, what is the right way?
Note: If I use the code
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self.attribute_name = value
end
I get the following error:
SystemStackError (stack level too deep):
actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
===========================================================================
Update: July 19, 2017
Now the Rails documentation is also suggesting to use super like this:
class Model < ActiveRecord::Base
def attribute_name=(value)
# custom actions
###
super(value)
end
end
===========================================================================
Original Answer
If you want to override the setter methods for columns of a table while accessing through models, this is the way to do it.
class Model < ActiveRecord::Base
attr_accessible :attribute_name
def attribute_name=(value)
# custom actions
###
write_attribute(:attribute_name, value)
# this is same as self[:attribute_name] = value
end
end
See Overriding default accessors in the Rails documentation.
So, your first method is the correct way to override column setters in Models of Ruby on Rails. These accessors are already provided by Rails to access the columns of the table as attributes of the model. This is what we call ActiveRecord ORM mapping.
Also keep in mind that the attr_accessible at the top of the model has nothing to do with accessors. It has a completely different functionlity (see this question)
But in pure Ruby, if you have defined accessors for a class and want to override the setter, you have to make use of instance variable like this:
class Person
attr_accessor :name
end
class NewPerson < Person
def name=(value)
# do something
#name = value
end
end
This will be easier to understand once you know what attr_accessor does. The code attr_accessor :name is equivalent to these two methods (getter and setter)
def name # getter
#name
end
def name=(value) # setter
#name = value
end
Also your second method fails because it will cause an infinite loop as you are calling the same method attribute_name= inside that method.
Use the super keyword:
def attribute_name=(value)
super(value.some_custom_encode)
end
Conversely, to override the reader:
def attribute_name
super.some_custom_decode
end
In rails 4
let say you have age attribute in your table
def age=(dob)
now = Time.now.utc.to_date
age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
super(age) #must add this otherwise you need to add this thing and place the value which you want to save.
end
Note:
For new comers in rails 4 you don't need to specify attr_accessible in model. Instead you have to white-list your attributes at controller level using permit method.
I have found that (at least for ActiveRecord relationship collections) the following pattern works:
has_many :specialties
def specialty_ids=(values)
super values.uniq.first(3)
end
(This grabs the first 3 non-duplicate entries in the array passed.)
Using attr_writer to overwrite setter
attr_writer :attribute_name
def attribute_name=(value)
# manipulate value
# then send result to the default setter
super(result)
end
I'd appreciate any help I can get with a somewhat strange phenonemon going on in my code. The controller's create method is (roughly) as follows:
def create
#session ||= Session.new
#session.date = params[:date]
#session.generate_string
#session.save
# etc
end
And the model:
class Session < ActiveRecord::Base # table is 'sessions' with 3 columns :id, :str, :date
include MyHelper
def generate_string(num_chars)
#str ||= ""
num_chars.to_i.times do
#str += some_method_in_MyHelper() # method returns a string
end
end
end
With some logging I found out that although the generate_string is working correctly, the resulting #session (in the controller) has the date set as expected but the value of str is a blank string. Sure enough, when the .save is hit, the database is told to insert a record consisting of a blank string and the correct date.
I found this Why do my changes to model instances not get saved sometimes in Rails 3? that suggests I should be using the "self" prefix instead of #. This seems to make the code work as expected, but seems strange because I thought self.xxx referred to the class, not the class instance. I'd be grateful if anyone could clarify what's going on - thanks!
self refers to the instance when used inside an instance method. It refers to the class outside an instance method, when it (self) is the class that's being defined.
# is an instance variable, which is different than an ActiveRecord column.
In order to store it in the str field to be saved to the database, you need to use self.str method. I think this is what you are looking for
class Session < ActiveRecord::Base # table is 'sessions' with 3 columns :id, :str, :date
include MyHelper
def generate_string(num_chars)
str = ""
num_chars.to_i.times do
str += some_method_in_MyHelper() # method returns a string
end
self.str = str # store it in attribute to be saved into db
end
end
Notice I removed the instance variable #str and changed it to local variable str instead because it seems like you want to generate a new string everytime this method is called? Also, this variable caching is useless
#session ||= Session.new
because instance variables only stick around for a single request. It should be
#session = Session.new