Rails has_many through query - sql

I have many-to-many relationship with User, Attachment and Form model.
I want to reach to attachments not belongs to a user. I will try something like this but did not work.
Attachment.includes(:forms,:users).where.not('forms.user_id = ?', #user.id).references(:forms,:users)
I have tried more but did't find the correct one.
user.rb
has_many :forms
has_many :attachments, through: :forms
attachment.rb
has_many :forms
has_many :users, through: :forms
forms.rb
belongs_to :user
belongs_to :attachment
Update:
I m still finding an answer
Attachment.includes(:forms).where(forms: {user_id: user.id}).references(:forms)
is working but where.not returns empty
I think where.not only looks the attachments related to forms not all of them

It's actually pretty simple:
The first (sub)query you will need is to get all the attachments the user does have:
subquery = #user.attachments.select(:id)
Then you can easily get all the attachments that don't have the id's from the subquery.
Attachment.where.not(subquery)
# same as
Attachment.where.not(#user.attachments.select(:id))
Leading to the query:
SELECT "attachments".*
FROM "attachments"
WHERE ("attachments"."id" NOT IN (
SELECT "attachments"."id"
FROM "attachments"
INNER JOIN "forms"
ON "attachments"."id" = "forms"."attachment_id"
WHERE "forms"."user_id" = $1
))

Related

Select the favorite groups

I have a model favorit_group (user:references, group:references).
And would like to find all the favorites groups of my User with something like that :
current_user.fav_groups
I suppose that i have to write something like this in my model User, but it's not working
has_many :favorit_groups, dependent: :destroy
has_many :fav_groups, through: :favorit_groups, class_name: "Group"
Is anyone who is working with this kind of association ? Or should I have to join and merge the model favorit_groups inside my User model ?
As the relation name fav_groups is not the same as the relation name between FavoritGroup and Group, Rails does not not how to fetch those records.
Luckily, has_many accept a source option:
has_many :fav_groups, source: :group, through: :favorit_groups, class_name: "Group"
See full explanation here: https://stackoverflow.com/questions/tagged/ruby-on-rails
Its look like there is many to many relationship between user and group
class User
has_many :favorit_groups, dependent: :destroy
has_many :fav_groups, through: :favorit_groups, source: :group
end
Try
current_user.groups which will automatical fire a join query and fetch those groups that are marked favorite by the use i.e those groups for which an entry is present in favorit_groups table

Where conditions on ActiveRecord associations conflicting with each other in has_many through?

In my app (Rails 4.2.0.rc2), users can be either students or admins of a given institution. There's an association :admin_institutions on User that returns all the institutions the user is an admin of by checking their role in the join table. There's also an association :students on Institution that returns all the users who are students at that institution, again according to institution_users.role.
These associations work as expected, so I added an association :admin_students to User, meant to return all the students at all the institutions for which a given user is an admin.
class InstitutionUser < ActiveRecord::Base
belongs_to :institution
belongs_to :user
end
class Institution < ActiveRecord::Base
has_many :institution_users
has_many :users, :through => :institution_users
has_many :students, -> { where "institution_users.role = 'Student'" }, :through => :institution_users, source: :user
...
end
class User < ActiveRecord::Base
has_many :institution_users
has_many :admin_institutions, -> { where "institution_users.role = 'Admin'" }, through: :institution_users, source: :institution
has_many :admin_students, through: :admin_institutions, source: :students
...
end
However, :admin_students does not work as expected. It generates the following SQL:
SELECT "users".* FROM "users" INNER JOIN "institution_users" ON "users"."id" = "institution_users"."user_id" INNER JOIN "institutions" ON "institution_users"."institution_id" = "institutions"."id" INNER JOIN "institution_users" "institution_users_admin_students_join" ON "institutions"."id" = "institution_users_admin_students_join"."institution_id" WHERE "institution_users_admin_students_join"."user_id" = $1 AND (institution_users.role='Student') AND (institution_users.role = 'Admin') [["user_id", 190]]
Instead of looking for all the institutions where the user is an admin and selecting all their students, it seems to be looking for institutions where the user is BOTH a student and an admin, so it returns an empty collection.
Is there a way to write an association (as opposed to just a method) that will give me the results I want, without my conditions conflicting like this?
(Side note: Is this the expected behavior for this kind of association? If so, I'd really appreciate further insight into why ActiveRecord interprets it the way it does.)
This may not be the answer, but maybe it will lead to one.
I'm not a fan of the associations with hard-coded SQL:
-> { where "institution_users.role = 'Student'" }
They are definitely at least part of the problem because they cannot be interpreted by ActiveRecord to determine which table alias for institution_users to apply it to.
You can allow ActiveRecord that flexibility by referencing a class method of the InsitutionUser model:
def self.students
where(role: "Student")
end
This also keeps the InstitutionUser logic all in one place.
Then the association becomes:
has_many :students, -> {merge(InstitutionUser.students)}, :through => :institution_users, source: :user
Perhaps try it with this and see if that sorts it out for you, and if not it might get things going in the right direction.

Querying relationships of relationships in Rails

I am trying to create a query in Rails but am having some trouble creating the correct one. Below is my models with their relationships.
class User < ActiveRecord::Base
has_and_belongs_to_many :rsvps, class_name: 'Event'
has_many :albums
end
class Event < ActiveRecord::Base
has_many :albums
has_and_belongs_to_many :attendees, class_name: 'User'
end
class Album < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
I need to get all events a user has "rsvp'ed" to that they haven't uploaded an album to yet. I can find out if a user has uploaded an album to a particular event using the following:
u = User.find(1)
e = Event.find(1)
e.albums.where(user_id: u.id)
I want to be able to run this query on each of the user's rsvp'ed albums. I know I could do something like this:
u.rsvps.delete_if { |e| !e.albums.where(user_id: u.id).blank? }
However, I want to do this all in one query instead of getting the rsvps and then iterating over them and deleting them when necessary.
In order to get all events a user has rsvp'ed to but haven't uploaded an album to yet, you can use the following, which (UPDATE) now also works when a user has not uploaded any albums.
#event_ids = Album.where(user_id: u.id).pluck(:event_id))
#event_ids.empty? ? u.rsvps : u.rsvps.where("id not in (?)", #event_ids)
In addition, this query should work as well.
u.rsvps.where.not(id: Album.where(user_id: u.id).pluck(:event_id))

Rails Search Self Referential Relationship

I am trying to allow users to search through their own friends by email address. I'd like to do something like:
current_user.search('test#fake.com')
and have it return an array of current users friends that have that email address.
So I have a very basic friendship relationship set up on my user model
user.rb
has_many :friendships
has_many :friends, through: :friendships, source: :friend
has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id'
has_many :inverse_friends, through: :inverse_friendships, source: :user
friendship.rb
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
belongs_to :user
I want to set up a method on my user model that can search through their friends by email address. It's not working so well
def search(query)
conditions = ['friends.user_id = ? AND email LIKE ? ', self.id, "%#{query}%"]
User.includes(:friends).where(conditions)
end
I guess I'm just not sure how to format my active record query / SQL here, since I am trying to search on the relations of a self referential model. Any one have any ideas?
Thanks!
Digital Cake is going in the right direction, but not exactly correct. A scope is a method of User, not user. What you need is:
def followers_by_email(email)
friends.where("email like ?", "%#{email}%")
end
This returns an ActiveRecord::Relation to which you can chain other conditions, order, paginate, etc as in
user.followers_by_email("me#example.com").order(:first_name).limit(10)
Good time to use active record scopes. http://guides.rubyonrails.org/active_record_querying.html#scopes
Heres a simple example
user.rb
scope :followers, friends.where(:published => true).order("created_at DESC").limit(150)
in your controller
#followers = User.followers
I seem to have some success with:
conditions = ['contacts.user_id = ? AND users.email LIKE ? ', self.id, "%#{query}%"]
User.includes(:inverse_friends).where(conditions)
Though it's strange that it works, I'm not entirely sure why it does.

Rails 3 - associations "through" - how to get the data from DB?

I have a problem with fetching data from DB, where is between models association kind through.
On my site, I have a categories, like a sports, news, weather etc. When an user is logged in and has a selected the categories, from which want to see the articles, then I would like to display only these articles.
Here's how looks like my models:
class User < ActiveRecord::Base
has_many :user_categories
has_many :categories, :through => :user_categories
end
class Category < ActiveRecord::Base
has_many :articles
has_many :user_categories
has_many :users, :through => :user_categories
end
class UserCategory < ActiveRecord::Base
belongs_to :user
belongs_to :category
end
class Article < ActiveRecord::Base
belongs_to :category
end
But I still can't find the way, how to get all articles from user's selected categories... I tried something like
Article.joins("LEFT JOIN categories ON category.id = user_categories.category_id").where('user_categories.user_id = ?', current_user.id)
I would grateful for every advice!
Thank you
Here's one way to do it:
Article.where(:category_id => current_user.categories.map {|c| c.id})
That will create 2 queries. First one will return a list of the current user's categories. Then the ruby map function will create an array containing the ids of those categories. The second query will then return a list of articles whose category_id is in the array of ids. The second query will look something like:
select articles.* from articles where articles.category_id in(1,2,3);