Query by chaining joins? - ruby-on-rails

Could you please help me out with this query? I still can't figure out how to achieve that.
There is a team model that has users and lists.
team
has_many :lists
has_many :users
And there is also a many to many relationship between users and lists that represents which lists the user has access to.
list
has_many :accesses
has_many :users, through: :accesses
belongs_to :team
user
has_many :accesses
has_many :lists, through: :accesses
belongs_to :team
access
belongs_to :list
belongs_to :user
I would like to get all the lists that belong to the users' team, but which user don't have access too.
I tried this, but that doesn't seem right:
List.joins(:team).joins(:user).where.not(users: { id: #user.id })
I also have a method called team.lists that allows to grab all lists of a team, but joins doesn't work on it.

What about splitting this a bit?
users_lists_ids = #user.lists.pluck(:id)
#user.team.lists.where('id NOT IN (?)', users_lists_ids)

Related

rails has_many through same destination

I have a User, Post and Like model. A user has many posts, and a user can like posts.
has_many :user_posts
has_many :users, through: :user_posts
has_many :likes
has_many :users, through: :likes
On the other side my rspecs are:
it { should have_many(:users).through(:user_posts) }
it { should have_many(:user_posts) }
it { should have_many(:likes) }
it { should have_many(:user_likes).through(:likes) }
This doesn't seem to go down very well, as rspec complains there already is a relationship through user:
Expected Post to have a has_many association called users (, Expected users to have users through likes, but got it through user_posts)
I tried all sort of combinations with class_name without much success. How should I define these relationships?
I'm making some inferences based on the code above. Let me explain the choices I made...
Your terminology around "user posts" is ambiguous to me, because it's unclear if a "user post" is something a user liked or something a user authored.
In my code, has_many :posts is referring to authorship and does not demand a join table because a post only has one author. On the other hand, has_many :liked_posts needs to go through: a join table (Likes) since it's a many-to-many relationship.
The only other thing that I've done differently is the use of the source: option in the association. Since a Like has no idea what either liked_posts or users_who_liked are, you need to tell it which of it's own associations to follow to get the records.
You can read more about Associations in the Rails API Documentation.
class User < ActiveRecord:Base
has_many :posts
has_many :likes
has_many :liked_posts, through: :likes, source: :post
end
class Like < ActiveRecord:Base
belongs_to :user
belongs_to: post
end
class Post < ActiveRecord:Base
has_many :likes
has_many :users_who_liked, through: likes, source: :user
end
Finally, as feedback on your question as-written: Tom's right in his comment above. It's not clear which classes those associations are a part of. Does a User have Users twice? Confusing. If I mis-inferred any of your business rules, let me know and I can revise my thoughts.

Get associated objects via DB access in ActiveRecord

I have 3 models: Guardian, Student and Organization. Guardian is connected to Student through a linking model and similarly Student is connected to Organization through a linking model. I need to get for every guardian, a list of (distinct) organizations and am wondering what the best way to do so is.
Currently I do it at the application level in the Guardian class
def organizations
orgs = []
students.each do |s|
s.organizations.each do |o|
orgs << o if !orgs.include?(o)
end
end
orgs
end
I wonder if there's a better way to do this, preferably at the database level. Any help will be appreciated.
Edit: here's a more detailed description of my models
class Person < ActiveRecord::Base
end
class Guardian < Person
has_many :student_guardian_links, inverse_of: :guardian, dependent: :destroy
has_many :students, through: :student_guardian_links, inverse_of: :guardians
end
class Student < Person
has_many :student_guardian_links, inverse_of: :student, dependent: :destroy
has_many :guardians, through: :student_guardian_links, inverse_of: :students
has_many :student_organization_links, inverse_of: :student, dependent: :destroy
has_many :organizations, through: :student_organization_links
end
class Organization < ActiveRecord::Base
has_many :student_organization_links, inverse_of: :organization, dependent: :destroy
has_many :students, through: :student_organization_links, inverse_of: :organizations
end
With relational databases is often better to think from the target set. So you want organisations with specific conditions (they have students connected to a specific guardian)
In ActiveRecord we have joins for this. It is a misnamer, as you still get only Organisation objects, it only uses a SQL-join to get them from the database, and you can specify conditions on the joined object.
Look at http://guides.rubyonrails.org/active_record_querying.html at "Joining Nested Associations" and "Specifying Conditions on the Joined Tables"
So depending on your exact model (remember you need the backward connections) it may look like this:
Organinisation.joins(:students).where('student.guardian_id' => mygivenguardian.id).distinct()
distinct is important when the join can lead to multiplications of the rows, as when a guardian is connected with more then one student to the organisation.
This should work:
students.map { |student| student.orgs }.flatten.uniq
Really the same approach you are taking. Just using a more functional approach.
Try this:
class Guardian < ActiveRecord::Base
has_many :organizations,
:through => :students
# Your existing relationships go here
end
This way you can simply call guardian.organizations.uniq to get a list of distinct organizations associated with a specific Guardian object.

Method to give number of favorites in has_many through relationship

I have a User model where the users can "favorite" each other. I'm achieving this through a Favoriting model as a has_many through relationship to reference User to itself:
class User < ActiveRecord::Base
has_many :favoriting
has_many :favorites, through: :favoritings, source: :favorited
has_many :favoriteds, class_name: "Favoriting", foreign_key: "favorited_id"
has_many :favoriters, through: :favoriteds, source: :user
...
end
class Favoriting < ActiveRecord::Base
belongs_to :user
belongs_to :favorited, :class_name => 'User'
...
end
This all works great. I can do u.favorites and get a user's favorites, and I can do u.favoriters to get the users that have favorited u. I can also do u.favorites_count to get the number of favorites.
However, I can't do u.favoriters_count to get the number of users that have favorited u.
Any idea if there is access to a built-in method for favoriters_count or even favoriteds_count with this type of DB relationship? I could write my own but would rather keep the code base as simple and "Rails-y" as possible.
Have you considered adding a counter_cache alongside with a favoritings_count column?
No, the methods added by has_many are listed in 4.3.1 of http://guides.rubyonrails.org/association_basics.html and do not include a method by this name.

has_many :through relationships explained

I'm new to Rails and have some doubts about the kind of relationship do I need to use. Here is the case.
I have two models Offer and User, a user could belong to to many offers and offers can have many user. Also the users create the offers.
I think I have to use a has_many :through ralationship. For example I've created another model "Applicant". Applicant belongs_to user and belongs_to offer. But how is the relationship from the user and offer model? For example:
User Model
has_many :offer, :through => :applicant
Offer Model
has_many :user, :through => :applicant
My doubt is because I already have this two relationship
User Model
has_many :offers, :dependent => :destroy
Offer Model
belongs_to :user
After solve this, I guest I have to save the record in the applicant model from the applicanst_controller, right?
Thanks in advance
What you have described is a many-to-many relationship using a join table. You're actually pretty close but you just need to remove the has_many :offers, :dependent => :destroy from your user model and the blongs_to :user in your offer model. It should look something like this:
class User < ActiveRecord::Base
has_many :offers, :through => :applicants
end
class Applicant < ActiveRecord::Base
belongs_to :users
belongs_to :offers
end
class Offer < ActiveRecord::Base
has_many :users, :through => :applicants
end
You don't have to worry about the dependent destroy part as associations are automatically removed as the corresponding objects are removed. With a many to many association it doesn't really matter how you go about building the relationship. Either of the following will work:
#user.offers << #offer
#offers.users << #user
If you don't need to store any information specific to your applicant join table (e.g., time stamps, descriptions) you might instead want to look at a has_and_belongs_to_many relationship. Check out choosing between has_many_through and has_and_belongs_to_many for reference.
Edit
Heres the code for a HABTM relationship:
class User < ActiveRecord::Base
has_and_belongs_to_many :offers
end
class Offer < ActiveRecord::Base
has_and_belongs_to_many :users
end

Ruby: ActiveRecord relationship

I have some problems setting up my desired relationship in my application. Some help and hints would be appreciated!
I have the following models:
User (id, username)
Company (id, name)
Campaign (id, name, company_id)
Relationship (user_id, company_id)
The relationship is supposed to connect the user to many companies.
Company has_many campaigns.
I want to to connect all the campaigns related to the companies that the specific user follows.
Users > (Relationships) > Companies > Campaigns
I'd better not post some code since it's just a mess and not at all doing what I want.
I've also really tried to follow railstutorial.org, http://ruby.railstutorial.org/chapters/following-users#top and change it the way I want with no success.
I need your help. :)
Should be pretty straightforward! This is obviously pseudocode, but here you go:
User
has_many :relationships
has_many :companies, :through => :relationships
has_many :campaigns, :through => :companies
Relationship
belongs_to :user
belongs_to :company
Company
has_many :relationships
has_many :users, :through => :relationships
has_many :campaigns
Campaign
belongs_to :company

Resources