I currently have a model for a vehicle, which has a custom method for defining an owner attribute:
def owners
#owners = sales.map(&:customer) + quotes.map(&:customer)
#owners = #owners.uniq
end
This method seems to work just fine, and returns an array of owners for the vehicle.
I am having a problem, however, when I want to use the owners that this method generates in another model. When I do this on another model:
has_many :owners, :through => :vehicles
This generates the error:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :owner or :owners in model Vehicle.
I have tried adding :source => :owners but I get the same error.
I should point out that I do have :owners in attr_accessible.
So, can I do a :through association when owners is defined in a custom method, rather than being a normal variable?
To answer your question directly, no.
Rails' has_many :through functionality is intended to work on ActiveRecord associations. Here, you're declaring an association to a table called owners through the vehicles table, but in actuality there is no owners table to join to: since you find the owners manually with an attribute, you aren't saving them as records in the database.
To get this working for you, I would just skip using ActiveRecord associations. Instead just define something like this on the model in question:
def owners
self.vehicles.collect(&:owners).flatten.uniq
end
You can use a through association if you start saving owners in your database, though, in which case the syntax you've provided will work.
The Rails Api says that the through: option expects you to specify an association through which to perform the query. It's trying to find this owners association based on the join table vehicles, but can't find it, since it's not there. I don't think your apporach can work without heavily diving into the HasMany-Builder from Rails.
Related
CONTEXT:
In my setup Users have many Communities through CommunityUser, and Communities have many Posts through CommunityPost. If follows then, that Users have many Posts through Communities.
User.rb
has_many :community_users
has_many :communities, through: :community_users
has_many :posts, through: :communities
Given the above User.rb, Calling "current_user.posts" returns posts with one or more communities in common with current_user.
QUESTION:
I'm looking to refine that association so that calling "current_user.posts" returns only those posts whose communities are a complete subset of the current_user's communities.
So, given a current_user with community_ids of [1,2,3], calling "current_user.posts" would yield only those posts whose "community_ids" array is either 1, [2], [3], [1,2], [1,3], [2,3], or [1,2,3].
I've been researching scopes here, but can't seem to pinpoint how to accomplish this successfully...
Nice question...
My immediate thoughts:
--
ActiveRecord Association Extension
These basically allow you to create a series of methods for associations, allowing you to determine specific criteria, like this:
#app/models/user.rb
has_many :communities, through: :community_users
has_many :posts, through: :communities do
def in_community
where("community_ids IN (?)", user.community_ids)
end
end
--
Conditional Association
You could use conditions in your association, like so:
#app/models/user.rb
has_many :posts, -> { where("id IN (?)", user.community_ids) }, through: :communities #-> I believe the model object (I.E user) is available in the association
--
Source
I originally thought this would be your best bet, but looking at it more deeply, I think it's only if you want to use a different association name
Specifies the source association name used by has_many :through
queries. Only use it if the name cannot be inferred from the
association. has_many :subscribers, through: :subscriptions will look
for either :subscribers or :subscriber on Subscription, unless a
:source is given.
Being honest, this is one of those questions which needs some thought
Firstly, how are you storing / calling the community_ids array? Is it stored in the db directly, or is it accessed through the ActiveRecord method Model.association_ids?
Looking forward to helping you further!
You don't show the model and relationship definitions for for Community or CommunityPost so make sure you have a has_many :community_posts and a has_many :posts, through: :community_posts on your Community model and a belongs_to :community and a belongs_to :post on CommunityPost. If you don't need to track anything on ComunityPost you could just use a has_and_belongs_to_many :posts on Community and a join table communities_posts containing just the foreign keys community_id and post_id.
Assuming you have the relationships setup as I describe you should be able to just use current_user.posts to get a relation that can be further chained and which returns all posts for all communities the user is associated with when you call .all on it. Any class methods (such as scope methods) defined your Post model will also be callable from that relation so that is where you should put your refinements unless those refinements pertain to the Community or CommunityPost in which case you would put them on the Community or CommunityPost models respectively. Its actually rare to need an AR relationship extension for scopes since usually you also want to be able to refine the model independently of whatever related model you may use to get to it.
I have one model say user, that can live in multiple towns (represented as another model). If I create a new user I have to choose (and edit) the different towns that they live in. Due to time constraints, I often end up with a "hackyier than I would like" solution involving something like: http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off.
Any nice solutions that are popular with SO?
cheers...
Slothishtype
The has_and_belongs_to_many association was built for this very situation. Here is the documentation on it: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Otherwise, if you need to store information abotu the association itself (fields that would not exist in the city table or the user table, but in between), you might just want to set up two, parallel 'has_many_through' associations, and set up a seperate 'user_city' table. So it would be in the user table
has_many :user_cities
has_many :cities, :through => :user_cities
and in the cities table
has_many :user_cities
has_many :users, :through => :user_cities
Then, you CAN just call: user.cities, and get a list of the cities the user lives in.
In my Rails app I have a has_many_through relation. I want to use the join model/table to store some data about the relation (how many times that particular relation is used, to be specific).
I'm writing an add-method on one of my classes which should check for any existing relation with the subject, update a counter on the relation if it exists or create it if it doesn't exist.
Example:
CoffeeDrinker is related to Coffee through Cup. Every time CoffeeDrinker takes a sip a counter on that particular Cup should be incremented. The first time CoffeeDrinker takes a sip, the Cup should be created and the counter initialized.
What is the easiest and/or most correct way to get a hold on the relation object?
Either I'm not understanding your question or you're going to be surprised how obvious this is.
You will have defined the relationship as so:
#coffee_drinker.rb
has_many :cups
has_many :coffees, :through => :cup
#cup.rb
belongs_to :coffee
belongs_to :coffee_drinker
#coffee.rb
has_many :cups
has_many :coffee_drinkers, :through => :cup
coffee_drinker.cups
coffee_drinker.coffees
coffee.cups
coffee.coffee_drinkers
#coffee_drinker.rb
after_save :update_cups_drunk
def update_cups_drunk
cups.find_by_coffee_id(coffee_id).increment!(:count)
end
#maybe you don't have access to the coffee_id
def update_cups_drunk
cups.find_by_coffee_id(coffees.last.id).increment!(:count)
end
I have the following entities:
User
Company
Organization
Users need to be able to add Users, Companies, Organizations, and future party objects to their friend list.
My original idea involved using a Friendship object with a polymorphic relationship to the friend like so:
Simplified Friendship schema:
user_id
friendable_id
friendable_type
User
- has_many :businesses, :through => :friendships, :conditions => ['friendable_type=?', 'Business'], :source => :friendable
My problem is that Ruby on Rails ActiveRecord does not support has_many through relationships through polymorphic relationships on the join table such.
In the end I want to have a single join table to be able to iterate through to get a list of friends of all types, certain types, etc. like below:
john = User.find(5)
john.friendships.map {|friendship| friendship.friendable }
john.businesses (has_many :through scoped to only the businesses)
john.organizations (has_many :through scoped only to the organizations)
I've considered making the User, Business, and Organization inherit from a generic Party class and making the association point to the base Party class, but in the context of an ORM that leads to a bunch of junk fields and it just seems dirty.
What I'd like to know is how others would approach a situation like this where you want to create one-to-many relationships to similar objects through a common join table using ActiveRecord while avoiding this error: :)
Cannot have a has_many :through association 'User#businesses' on the polymorphic object 'Friendship#friendable'.
Any advice greatly appreciated.
Thanks!
Do not use :through, use :as=>friendable instead in polymorphic has_many relationship
It appears that the Rails plugin 'has_many_polymorphs' allows me to do exactly what I want.
http://github.com/fauna/has_many_polymorphs/tree/master
Adding this line to my User model gave me the functionality I wanted:
has_many_polymorphs :friendables, :from => [:businesses, :organizations], :through => :friendships
Sorry for the vague title, but this is kind of hard to put into one line.
I have a database full of contact information, and I want to be able to put those different contacts into groups which will also be stored in the database.
So, maybe I have 2 groups, "coworkers" and "neighbors", I want to be able to see a list of all my contacts and be able to add individual contacts to one or more groups.
I'm kind of confused as where to begin with this, could I get some basic outlines of how this might best be implemented? Thanks.
Well, you've got two models, Contact and Group. These two guys are clearly going to have their own tables (probably 'contacts' and 'groups' if you're following the Rails conventions). Since a contact can be in many groups, and a group can have many contacts, you've got what's called a many-to-many relationship between these models. There are basically two ways to implement this in Rails: has_and_belongs_to_many or has_many :through. Which one is best for you will depend a little on your situation.
has_and_belongs_to_many is probably the path of least resistance. You put a couple lines in your models like so:
# contact.rb
has_and_belongs_to_many :groups
# group.rb
has_and_belongs_to_many :contacts
...and create a table called 'contacts_groups' with the columns contact_id and group_id, and you're basically good to go. Easy peasy.
On the other hand, there are some advantages to using an association model with has_many :through. In this approach, you create another model, say, GroupMembership, and set up your models like so:
# contact.rb
has_many :group_memberships # this isn't strictly required, but I'd recommend it
has_many :groups, :through => :group_memberships
# group_membership.rb
has_many :groups
has_many :contacts
# group.rb
has_many :group_memberships # again, recommended but not required
has_many :contacts, :through => :group_memberships
This gives you most of the same convenience methods as has_and_belongs_to_many, and also lets you store any extra data you might want about the association, like the date they joined the group or the reason they were added. Even if you don't have any of that now, it's nice to account for the possibility of adding it later. Also, this lets you take a more RESTful approach to adding and removing contacts to and from groups, if that's something you're interested in, since you can model it in terms of creating and destroying GroupMembership resources.
By and large, I tend to lean toward the latter approach, especially as I've gotten more in to RESTful architectures. On the other hand, if you're not worried about REST and you're certain you'll never want to keep any extra information about memberships, has_and_belongs_to_many is probably simpler and requires less code to get working. For more in-depth information on the differences and implementation details, see the ActiveRecord Associations API docs.
You could structure a Ruby on Rails database to be :
Contact ( first_name:string, last_name:string, title:enum number:string, cell:string, notes:text, email:string ) => many_many :groups (or has_many :groups, :through=> :contact_group)
Contact_Group { group_id:integer, contact_id:integer }
Group ( name:string ) => many_many :contacts ) (or has_many :contacts, :through=> :contact_group)
That might be the general idea. You also could do relational fields.
You don't want to use the has_and_belongs_to_many approach. It's deprecated. Please read the API and make sure you implement a join model / has_many :through approach. The API will tell you how to do this and also mention why has_and_belongs_to_many is bad.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html