New in rails here. I have trouble understanding this specific activerecord association. Can someone help me on this. The model looks like this:
class User < ActiveRecord::Base
has_many :client_occurrences,
foreign_key: "client_id",
class_name: "Occurrence"
has_many :requested_occurrences,
foreign_key: "requestor_id",
class_name: "Occurrence"
end
And the one it's associated to is:
class Occurrence < ActiveRecord::Base
belongs_to :template, autosave: true
belongs_to :requestor, class_name: "User"
belongs_to :client, class_name: "User"
end
I just can't seem to understand the associations being portrayed here. Everytime I see the user model, I immediately classify it as an issue because here's how I read the association in the user model:
User has many occurrences alias by client_occurrences and set
client_id as foreign_key
It's an issue for me since the foreign_key is not in the proper table (According to my understanding of the code). In addition, client_id and requestor_id are columns found in the Occurrence table.
Could anyone help?
I'm not sure where your issues are. I would say your reading is correct, namely:
User does have many Occurences (each Occurence points back to
the User)
They are aliased/referenced as client_occurrences from
the perspective of the User The foreign_key is indeed
client_id.
That is, the Occurence table uses client_id to point
to the User
From the point of view of Occurrence:
Each Occurrence belongs to a :client, which means the field name will be client_id (which matches the foreign_key clause in the User model)
The item being pointed to is really a User
One of the things that's confusing, I think, is that the order of the has_many clauses is different from the order of the corresponding belongs_to clauses.
These are the business rules I gather from that:
A User can be associated with an Occurrence as a client
A User can be associated with an Occurrence as a requestor
A User can be associated to many Occurrences
An Occurrence has one requestor User, and one client User
The foreign key is specified in the User model because it's associated to the same model multiple times, otherwise rails would default to using "user_id" as the foreign key in the Occurrence model.
Check this link out for the full details on what all the different ActiveRecord Associations do:
Rails Guides: ActiveRecord Associations
Related
My Seminar model has a column named teacher_id. That part of the relationship is working. I can call seminar.teacher to retrieve the User who teaches that seminar.
I'd like to be able to invert that query. In other words, I need to call teacher.own_seminars to get a collection of all the seminars where that User is listed as the teacher_id. I know that I could call Seminar.where(:teacher => teacher), but that's clunky. And I think that the performance is probably worse that way.
Note: Some of the Users are students who are linked to Seminar through the seminar_user join table, but I don't think that affects this question.
This is the model setup that isn't quite working yet
class User < ApplicationRecord
has_many :own_seminars, :class_name => "Seminar", foreign_key: 'own_seminar_ids'
end
class Seminar < ApplicationRecord
belongs_to :teacher, class_name: "User", foreign_key: 'teacher_id'
end
Cheers!
In foreign_key option, you specify the column which is, well, the foreign key.
The way has_many works, is it tries to guess, which one of the fields in the referenced entity corresponds to the primary key of this entity. By default, it's user_id (derived from name User). But since your column is actually called teacher_id, you should use that instead.
has_many :own_seminars,
class_name: "Seminar",
foreign_key: 'teacher_id'
In the app I am building to learn RoR, I have a model "Document" and a model "Business Partner". On the "Document" model, I have 2 fields ("Sender" and "Receiver") referring to the model "Business Partner".
How do I model twice a belongs_to the same target with different fields? What should the migration be?
Related question: how do I model the relationship to it self for business partner? i.e. one company has many business partners, yet can be a business partner too. Note - not to the same record (company A cannot have a relationship to company A (itself).
Assuming you've got columns sender_id and receiver_id for the sender and receiver relationships on your Document model (i.e. on your documents table), you can do something like this:
class Document < ActiveRecord::Base
belongs_to :sender, class_name: "BusinessPartner"
belongs_to :receiver, class_name: "BusinessPartner"
end
There's no particular migration for this as long as you've got those columns there on the table (if you've named them something else, just replace sender and receiver above by whatever column name minus the _id part).
And then for your BusinessPartner model:
class BusinessPartner < ActiveRecord::Base
has_many :sent_documents, class_name: "Document", foreign_key: "sender_id"
has_many :received_documents, class_name: "Document", foreign_key: "receiver_id"
end
Here, sent_documents will fetch all rows in the documents table where sender_id matches the id of the BusinessPartner, and similar for received_documents.
More info in the Rails docs.
Regarding your second question, there is a section of the Rails docs describing this, it is called "Self Joins". However, given that you want to model a many-to-many relationship, you will need a slightly special table arrangement. See this SO answer for some details on how to setup that arrangement. That is actually a somewhat tricky topic in itself, if you're interested in details I'd recommend asking a separate question on it (although that SO post answers it pretty well).
TL;DR: How do I use the ID of the respective parent object in a has_many SQL clause to find child objects?
Long version:
I have the following example code:
class Person < AR::Base
has_many :purchases, -> {
"SELECT * from purchases
WHERE purchase.seller_id = #{id}
OR purchase.buyer_id = #{id}"
}
This was migrated from Rails 3 which worked and looked like
has_many :purchases, :finder_sql => proc { #same SQL as above# }
I want to find all purchases associated with a Person object in one association, no matter whether the person was the one selling the object or buying it.
Update: I corrected the SQL, it was inside out. Sorry! Also: The association only needs to be read-only: I am never going to create records using this association, so using id twice should be OK. But I do want to be able to chain other scopes on it, e.g. #person.purchases.paid.last_5, so creating the sum of two associations in a method does not work (at least it didn't in Rails 3) since it doesn't return an AM::Relation but a simple Array.
When using this above definition in Rails 4.2, I get
> Person.first.purchases
undefined method `id' for #<Person::ActiveRecord_Relation:0x...>
The error is clear, but then how do I solve the problem?
Since this is only an example for much more complicated SQL code being used to express has_many relationships (e.g. multiple JOINS with subselects for performance), the question is:
How do I use the ID of the parent object in a has_many SQL clause?
I don't think your code will work at all. You are defining an association with two foreign keys ... that'd mean that in case you want to create a new Person from a present Purchase, what foreign key is to be used, seller_id or buyer_id? That just don't make sense.
In any case, the error you are getting is clear: you are calling a variable id which is not initialized in the block context of the SQL code.
A better approach to the problem I understand from your question would be to use associations in the following way, and then define a method that gives you all the persons, both buyers and sellers that a product has. Something like this:
class Purchase < ActiveRecord::Base
belongs_to :buyer, class_name: 'Person'
belongs_to :seller, class_name: 'Person'
def persons
ids = (buyer_ids + seller_ids).uniq
Person.where(ids: id)
end
end
class Person < ActiveRecord::Base
has_many :sold_purchases, class_name: 'Purchase', foreign_key: 'buyer_id'
has_many :buyed_purchases, class_name: 'Purchase', foreign_key: 'seller_id'
end
Im my approach, buyer_id and seller_id are purchase's attributes, not person's.
I may have not understood correctly, in that case please clarify.
So I have an application with users/user-profiles. Presently, users can view others profiles, send messages, and favorite user profiles. I'd like to also add the feature to allow users to 'hide-user' profiles so that they won't see the user ever again in search, or anything. And provide the option in 'settings' to 'un-hide' the user as well.
How might I do this? I haven't a clue as to where to begin with this feature.
(If you need any code examples, models, controllers please ask and I will happily provide)
Kind regards,
Sonny
There are probably a couple of ways to do this, the first approach that comes to mind would be to establish a self-referencing many to many relationship.
You will need to create the join table (I shall call it suppressed_users). I will show the rails model, as the migration wouldn't have anything other than the foreign keys.
class SuppressedUser < ActiveRecord::Base
belongs_to :user
belongs_to :suppressed_user, :class_name => "User", :foreign_key=>"suppressed_user_id"
end
And in the User model, in order to help DRY up your code, you can use a scope to easily filter all the users that this target user has decided to suppress (or hide):
class User < ActiveRecord::Base
has_many :suppressed_users // Optional
scope :without_hidden_users, -> (target_user) do
where.not("exists (?)",
SuppressedUser.select("1")
.where("users.id = suppressed_users.suppressed_user_id AND suppressed_users.user_id = ?", target_user))
end
end
Note about the scope: What I'm doing here is creating a dependent (or correlated) subquery, in which I check whether the target user has suppressed (or hidden) the user we're looking at at the moment. In other words, the dependent subquery is executed for each row in the users result set (those not filtered by other where or join conditions and such). With proper indexing, this should not have an impact on performance.
Note about has_many :suppressed_users: This is technically not needed for the query I've shown, so if it is not relevant for anything in your system, you should be safe to remove it.
So, if I am presently logged in, and I want to search for a list of users meeting some condition, in your controller you would do something like this:
User.without_hidden_users(#current_user.id)...other conditions and such as needed
Assuming #current_user represents the currently logged in user.
I believe one approach would be to create a many_to_many relationship via the has_many :through association between users and hidden users. And likewise another many_to_many relationship between users and hiders (i.e. users who are hiding the user). In the end, you should be able to do something like this:
some_user.hidden_users and some_user.hiders (i.e. all the users that are hiding some user). You probably won't need the second one most of the time.
So the first thing to do would be to create a join table called hiders_hidden_users (or whatever you want) which would contain only two fields: hider_id and hidden_user_id.
Note, that in reality, those ids will both refer to a User record. Your HidersHiddenUser model will look something like this:
class HidersHiddenUsers < ActiveRecord::Base
belongs_to :hidden_user, class_name: "User", foreign_key: "hidden_user_id"
belongs_to :hider, class_name: "User", foreign_key: "hider_id"
end
Then, you need to set up the relationships in the User class:
class User < ActiveRecord::Base
has_many :hiders_join, class_name: "HidersHiddenUser", foreign_key: "hider_id"
has_many :hidden_users_join, class_name: "HidersHiddenUser", foreign_key: "hidden_user_id"
has_many :hiders, through: :hiders_join
has_many :hidden_users, through: :hidden_users_join
end
Note, that you have to specify the class name and foreign key when writing the has_many relationship with the join table. Note, also that you have to specify the relationship twice with the same model.
Then, say some_user wants to hide user1 and user2. All you would need to do is something like this:
some_user.hidden_users << user1
some_user.hidden_users << user2
Although I have not tested this, I believe it should work.
So I have a DB relationship where a User has a PreferenceList. I have set up a preference_list_id in the database table, and I have set up a belongs_to :user relationship with PreferenceList and a has_one :preference_list relationship with User.
Here's the weird part. If I do user.preference_list = preference_list, I can access user.preference_list and it will give me the correct instance of preference_list. However, if I do user.preference_list_id (a valid, column, I've checked), it gives me nothing even after I have added preference_list to user.preference_list. I need the id to be updated for various database operations. What am I doing wrong?
The foreign key goes on the table for the class declaring the belongs_to association. So if you have
class PreferenceList < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :preference_list
end
table preferences_lists should have user_id columns. Rails will handle this columns properly. You can add preference_list_id column to users table as well. But this column has no special meaning, Rails will do nothing with it by default. Try to change belongs_to/has_one sides if you really need to operate preference_list_id. Or use user.preference_list.id