For example, have the models:
class Activity < ActiveRecord::Base
belongs_to :event
end
class Event < ActiveRecord::Base
has_many :activities
attr_accessible :foo
end
I can get the activity's event foo by using activity.event.foo (simple enough).
But I want to make a generic function that finds out first if an object has a belongs_to association and then get that object's foo through the belongs_to association (pretend that all objects have a foo through the belongs_to association)?
So far I have the following with gives me a reflection:
def get_foo(object)
object.class.reflect_on_all_associations(:belongs_to).each do |belongs_to|
return object.??????
end
end
I can either get an array of the reflection's class name via belongs_to.klass (e.g. [Event]) or an array of symbols for the belongs_to association via belongs_to.name (e.g. [:event]).
How do I get the object's belongs_to's foo given what I get from the reflection?
Is there an easier way to do this without using the reflection?
I'm hoping this is something simple and I'm just spacing out on how to solve this. I also hope I am being somewhat clear. This is my first Stack Overflow question.
You can do this but its not exactly pretty:
def get_foo(object)
object.class.reflect_on_all_associations(:belongs_to).map do |reflection|
object.send(reflection.name).try(:foo)
end
end
That will give you an array of all the foos associated with the object. You can change it to not do map and do a first or something.
Related
I have the following models:
class Post < ApplicationRecord
has_many :metrics
end
class Metric < ApplicationRecord
belongs_to :post
end
I would like to know why there isn't a create_metrics method in a post instance. If the relationship was:
class Post < ApplicationRecord
has_one :metric
end
class Metric < ApplicationRecord
belongs_to :post
end
There would be a method create_metric in a post instance.
There is a collection.create method specified here: https://guides.rubyonrails.org/association_basics.html#methods-added-by-has-many-collection-create-attributes
In this particular case, you can call:
#metric.posts.create
And pass an array of objects with the data you want.
If you flip this question on its end you can see that for belongs_to and has_one you have the build_other and create_other methods since the association is nil by default.
So if you called thing.other.create you're calling .create on nil. Not good. While you could get around it by creating some kind of proxy object that would break code that relies on it being nil.
has_many and has_and_belongs_to_many don't have this problem since an empty association is an AssociationProxy object. You can think of this as kind of like an empty array. And even empty arrays have methods.
Its far more natural to call foo.bars.new or foo.bars.create than some metaprogramming method. And much easier to find the correct documentation.
Not sure why the convenience methods aren't there, but there is definitely a post.metrics.create(...) method. Most of these dynamic methods are on the collection itself, not the original model. See the official Rails ActiveRecord Associations Guide for more details.
I am trying to save an array of multiple ids (item_variation_ids) to a model called items_stock from item variations model. In a column called item_variation_ids in item_stock, it is saving the ids like [1,2,3] for twice. I want the item_variation_ids to be saved once only with 1,2,3 in a single column.
My item_variation model
#app/models/item_variation
class ItemVariation < ApplicationRecord
belongs_to :item
validates_associated :item
after_save :add_to_item_stock
def add_to_item_stock
ItemStock.create(item_variation_ids: ItemVariation.ids, items_id: items_id)
end
end
My item model
#app/models/item
class Item < ApplicationRecord
has_many :item_variations, foreign_key: :items_id
has_many :item_stocks, foreign_key: :items_id
accepts_nested_attributes_for :item_stocks
end
My item_stock model
#app/models/item_stock
class ItemStock < ApplicationRecord
belongs_to :item
end
But how do you know which ItemVariation ids should go on that ItemStock? and you are creating one ItemStock each time any variation gets saved. I don't even think you need to set that ids array since the ItemStock already belongs to an Item which has many variations (#item_stock.item.variations and you are done).
Also now you are talking about a stock_qty attribute you never mentioned before, you are never setting it on the callback and you didn't show your database schema. where does that amout come from? is an attribute on the variation that you want to sum to the current item_stock?
I also don't understand why an item has many item stocks for the code you are showing.
I'll do a wild guess and suggest you do something like:
ItemStock
belongs_to :item
belongs_to :item_variation
end
ItemVariation
after_save :add_to_item_stock
def add_to_item_stock
item_stock = self.item.item_stock.where(item_variation_id: self.id).first_or_initialize
item_stock.stock_qty = self.stock_qty
item_stock.save
end
end
but as I said, it's a wiiiiild guess. I'd recommend you to first try to understand what you are doing, because it seems like you just copied to code from that question you linked and you are no actually understanding it.
For example I have
class Order < ActiveRecord::Base
has_many :shippings
has_one :contact_information
belongs_to :shop
end
How to get an array of associated objects from Order. For example
Order.associations
# [:shipping, :contact_information, :shop]
Order.reflect_on_all_associations.map(&:class_name)
You can pass a type of relation as a parameter:
Order.reflect_on_all_associations(:has_one)
Read about ActiveRecord::Reflection::ClassMethods
EDIT
Just realised, you've asked about object's associated models.
So, having what I've already shown, you can simply do something along the following lines:
associated_models = Order.reflect_on_all_associations.map(&:class_name)
Associations:
class Foo
has_many :foo_subscribers
end
class FooSubscriber
belongs_to :foo
belongs_to :user
end
class User
has_many :foo_subscribers
belongs_to :employee
end
class Employee
has_one :user
end
The following code is adding a User object to the array, despite the explicit call to User's Employee
#foo.rb
employees_to_notify = Array.new
foo_subscribers.each do |user|
employees_to_notify << user.employee
end
All of the appropriate data is present for the test. Through debugging, I can evaluate user.employee as an employee object right on the line where it's added to the array.
How is this possible? I've tried various things like iterating over the array and converting any User objects to Employees. That also failed.
It looks like you are iterating over foo_subscribers, but then naming them as users in the pipes? Are foo_subscribers users?
I was trying to get foo_subscriber.employee rather than foo_subscriber.user.employee. I was attempting to perform an operation on the wrong type.
I came across some magic today and I am hoping for some help in understanding it so I can write informed code.
In my app, I have three classes:
class Person < ActiveRecord::Base
has_many :selected_apps
has_many :app_profiles, through: :selected_apps do
def unselected(reload=false)
#unselected_app_profiles = nil if reload
#unselected_app_profiles ||= proxy_association.owner.app_profile_ids.empty? ?
AppProfile.all :
AppProfile.where("id NOT IN (?)", proxy_association.owner.app_profile_ids)
end
end
end
class AppProfile < ActiveRecord::Base
end
class SelectedApp < ActiveRecord::Base
belongs_to :person
belongs_to :app_profile
end
The above code lets me do person.app_profiles.unselected and get back all of the AppProfiles that are not currently associated with the Person without having to do a lot of SQL work. Brilliant!
My problem is that I don't understand the code - which always leaves me feeling unsettled. I tried trolling through the proxy_association documentation, but it was fairly opaque.
Can anyone provide a reasonably straight-forward explanation and/or a good place to learn more?
Basically, when you call self while extending an association it won't return an Association instance, but instead delegates to to_a.
Try it:
class Person < ActiveRecord::Base
has_many :app_profiles, through: :selected_apps do
def test_me
self
end
end
end
Sometimes we need to get to the actual association object while were extending the association itself. Enter the proxy_association method, which will give us the association which contains the owner, target, and reflection attributes.
For reference here is the documentation.
This question provides a simpler use case of proxy_association.owner.