Rails how to find id by another attr in a callback model? - ruby-on-rails

I'm looking to create a callback where update a object if find the id attribute of another model.
in this case if find update Odata model if find the order_id.
someone know how to find the object based on another model id attribute?
class Order < ActiveRecord::Base
after_update :update_odata
def update_odata
order = Order.find_by_id(attributes['id'])
od = Odata.find_by_id(attributes['order_id'])
od.shipping_cost = order.shipping_cost
od.shipping_method = order.shipping_method
od.status = order.status
od.feedback_id = order.feedback_id
od.track_number = order.track_number
od.seller_name = order.seller_name
od.buyer_name = order.buyer_name
od.save
end
end

In general you should check the docs and at least make an effort to learn the tools you're using before resorting to asking for someone to help explain it to you on StackOverflow.
To answer your question, find(1) is effectively a shortcut method for find_by(id: 1). Thusly, if you want to find an order by customer_id you could do this: Order.find_by(customer_id: 42).
Or, if you're trying to make this contingent on order (making some assumptions based on how Rails apps are built vs this unusual attributes stuff you have in your example):
order = Order.find(params[:id])
od = Odata.find_by(order_id: order.id)
In which case, you should probably just use relations:
class Order < ApplicationRecord
has_one :odata
end
class Odata < ApplicationRecord
belongs_to :order
end
# controller:
order = Order.find params[:id]
od = order.odata
If you wanted to do exactly what you are above, which is probably a bad path to go down, you would probably want to do something like this:
class Order < ApplicationRecord
has_one :odata
def attributes_for_odata
%w{ shipping_cost shipping_method status feedback_id track_number seller_name buyer_name }
end
def update_order_data
odata.update attributes.slice(*attributes_for_odata)
end
end

Related

Rails setter method that updates two other fields?

Background
I have some polymorphic relationships - one in particular assetable where I have a parent-child Asset relationship and the parent has various classes.
I also have a global Tracker model that creates a global id type scheme across my various models which I also use the FriendlyID gem for.
My Goal
Something like this:
parent = Tracker.find_by(tracker: '4Q73XEGK').trackable
Asset.find(1).update(parent_tracker: parent.tracker_id)
thinking I could do something like this - set the new polymorphic relationship by the tracker_id:
class Asset < ActiveRecord::Base
def parent_tracker
assetable.tracker_id
end
def parent_tracker=(val)
a = Tracker.find_by(tracker: val).trackable
assetable_id = a.id
assetable_type = a.class.name
end
end
Question?
Am I on the right path here (with tweaks) OR show that I am on the completely wrong path here.
I know that there are before_save filters etc. but the setter approach seems more elegant and clear as I can apply this across many other models.
You should not have to set both the type and id - use the setter created by the association instead:
class Asset < ActiveRecord::Base
belongs_to :assetable, polymorphic: true
def parent_tracker
assetable.tracker_id
end
def parent_tracker=(val)
self.assetable = Tracker.find_by(tracker: val).trackable
end
end
The setter for polymorphic associations will set both the id and type attributes. Also note that you need to use self explicitly when calling setters.
assetable_id = a.id
assetable_type = a.class.name
Will just set local variables that are garbage collected when the method ends.
Tracker.find_by(tracker: val) feels really smelly too. If your Tracker class just keeps track of global ids shouldn't it provide a method that takes such an id and returns the trackable?
class Tracker < ActiveRecord::Base
def self.lookup(global_id)
find_by(tracker: global_id).trackable
end
end
class Asset < ActiveRecord::Base
belongs_to :assetable, polymorphic: true
# ...
def parent_tracker=(val)
self.assetable = Tracker.lookup(val)
end
end

How to properly assign value to model column?

In my console I have one thing that is working and I need to make a method for it.
#sale.weight = #sale.fisherman.fish.sum(:weight)
Here I assign right part to the column weight on model Sale.
The question is how to write a method for this?
The following approach didn't work out for me. In this case the column remains nil after calculation.
class Sale < ActiveRecord::Base
def calculate_total_weight
weight = fisherman.fish.sum(:weight)
end
end
Ok, need to use self, otherwise weight will be treated as local variable. Like :
class Sale < ActiveRecord::Base
def calculate_total_weight
self.weight = fisherman.fish.sum(:weight)
end
end
Follow Why does attr_accessor in module require 'self.'? to know the reason of using self explicitly.

Ruby on Rails, uninitilized constant after renaming model

I'm very new to RoR.
I can use this source and calling "UsersFriends.test" with no problem.
class UsersFriends < ActiveRecord::Base
belongs_to :user
module Status
UNREAD = 0; READ = 1; GET = 2
end
def self.test
Time.now.strftime('%F')
end
end
Now I want to call it as "Friend" in console. So, I added "Friend::" in front of UsersFriends.
But I get "uninitilized constant Friend" when I use the following source.
class Friend::UsersFriends < ActiveRecord::Base
belongs_to :user
module Status
UNREAD = 0; READ = 1; GET = 2
end
def self.test
Time.now.strftime('%F')
end
end
Could you please explain me how to solve this problem?
Thank you.
You cannot directly do like this. If You want this kind of behaviour you should do few changes in app directory structure.
Create a Folder named friend and in that folder put the usersfriends.rb file.
app/models/friend/usersfriends.rb
(create a model named friend if necessary or else use user model and refer the same using user instead of friend)
Then, use
class Friend::UsersFriends < ActiveRecord::Base
You can find a good article of Model name spacing, here in this link

Ruby on rails associations parents

I would like to know, whatever the association is (simple belongs_to, polymorphic ...), when I make an association like :
class Toto < ActiveRecord::Base
belongs_to :test_one
belongs_to :test_two
end
class TestOne < ActiveRecord::Base
has_many :totos
end
class TestTwo < ActiveRecord::Base
has_many :totos
end
and then
test_one = TestOne.create
test_two = TestTwo.create
test1 = test_one.totos.create
test2 = test_two.totos.create
I would like to know into a callback of Toto what object instantiate me. In this case, it's obviously test_one and then test_two. I know I could check ids for example but the problem is when i do :
test3 = test_one.totos.create(test_two: test_two)
I can't know if test3 was created through test_one or test_two.
Thank you.
According to your example, I understand that you want to identify the type of object which is associated to your totos object (has_many :totos).
Since there are multiple different objects that might be associated to your totos object through the has_many and belongs_to associations, you might want to perform some kind of verification first to identify the type of the associated object.
First Answer:
This will only work if you know beforehand all the object types that has_many :totos
if test3.respond_to?(:test_one)
test = test3.test_one
elsif test3.respond_to?(:test_two)
test = test3.test_two
end
Second Answer:
I found this on Stackoverflow, and it somehow answeres your question. So if I rephrase the answer to:
def get_belongs_to(object)
associated = []
object.class.reflect_on_all_associations(:belongs_to).map do |reflection|
associated << object.try(reflection.name)
end
associated.compact
end
This method will return an array of all objects associated to your totos object. This will also work when totos belongs to multiple objects say test_one and test_two at the same time. So the following:
associated_objects = get_belongs_to(test3)
and in your case associated_objects[0] will yield the object you desire.
Hope this helps.
Rails does not persist the data you're looking for, so you'll have to store it yourself if you want it. This means you'll need a migration for the new field:
rails generate migration AddOriginalParentTypeToTotos original_parent_type:string
rake db:migrate
You can then override the assignment methods so that the first parent assigned will assign the original_parent_type attribute (and it will remain the same once assigned):
class Toto < ActiveRecord::Base
def test_one=(val)
self[:original_parent_type] ||= 'test_one'
super
end
def test_one_id=(val)
self[:original_parent_type] ||= 'test_one'
super
end
def test_two=(val)
self[:original_parent_type] ||= 'test_two'
super
end
def test_two_id=(val)
self[:original_parent_type] ||= 'test_two'
super
end
end
You can then use send to add an original_parent method:
class Toto < ActiveRecord::Base
def original_parent
send(original_parent_type) if original_parent_type
end
end

rails call back update database

I have a Task Order that has_many Invoices. Task Order has an attribute called "invoicedAmount". Invoice has an attribute called "amount". I am trying to make a callback where whenever I add or delete an invoice, the "invoicedAmount" field updates accordingly. Right now I am able to make it calculate on the fly, but now I want to save it to the database. Here is my Invoice model:
class Invoice < ActiveRecord::Base
belongs_to :task_order
validates_presence_of :task_order_id
after_save :update_task_order_invoiced_amount, notice: ':)!'
after_destroy :update_task_order_invoiced_amount
def update_task_order_invoiced_amount
#task_orders = TaskOrder.all
#invoices = Invoice.all
#task_orders.each do |task_order|
task_order.invoicedAmount = task_order.invoices.sum(:amount)
end
end
end
Any guidance would be greatly appreciated!
You probably don't want to recalculate all TaskOrder records, but only changed one. So your update_task_order_invoiced_amount should look something like this:
def update_task_order_invoiced_amount
task_order.invoicedAmount = task_order.invoices.sum(:amount)
task_order.save
end

Resources