Rails join or includes? - ruby-on-rails

I've got a User model/table and a Friend model/table. The users table has a uid column that stores a user's Facebook uid. The friends table stores their Facebook friends (associated by a user_id column).
How can I query all of a specific user's friends who have an account in the users table? In other words - I want the user models returned for user 13's friends who have signed up (exist in the users table).

My models for same problem
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, through: :friendships
# ...
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User', :foreign_key => 'friend_id', :primary_key => 'facebook_id'
end

Assuming you the user of interest is referenced by #user:
User.joins("inner join friends on friends.uid = users.uid")
.where("friends.user_id = ?", #user.id)
There is probably a more "railsy" way to do this that makes better use of arel.

If I have the models:
class User < ActiveRecord::Base
attr_accessible :name, :uid
has_many :friends
end
class Friend < ActiveRecord::Base
attr_accessible :uid
belongs_to :user
belongs_to :account, foreign_key: 'uid', primary_key: 'uid', class_name: 'User'
end
then I can do:
u=User.create(name: 'Alice', uid: 'fb1')
u2=User.create(name: 'Bob', uid: 'fb2')
u.friends.create(uid: 'fb2') # add Bob as friend
u.friends.create(uid: 'fb9') # add someone as friend
Now
u.friends # gives all friends
Friend Load (0.3ms) SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 4
=> [#<Friend id: 3, user_id: 4, uid: "fb2">, #<Friend id: 4, user_id: 4, uid: "fb9">]
u.friends.joins(:account) # only friends having an account
Friend Load (0.2ms) SELECT "friends".* FROM "friends" INNER JOIN "users" ON "users"."uid" = "friends"."uid" WHERE "friends"."user_id" = 4
=> [#<Friend id: 3, user_id: 4, uid: "fb2">]
while includes doesn't do an inner join and gives all friends:
u.friends.includes(:account)
Friend Load (0.2ms) SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 4
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."uid" IN ('fb2', 'fb9')
=> [#<Friend id: 3, user_id: 4, uid: "fb2">, #<Friend id: 4, user_id: 4, uid: "fb9">]

Related

How to do N: N join in rails

user table
user.find(1)
id: 3,
name: 'hoge',
gender: 0
org_users table
user.find(1).org_users
id: 1,
org_id: 1,
user_id: 3,
status: 'in progress'
I would like to do inner join the above two tables as follows:
id: 3,
name: 'hoge',
gender: 0,
status: 'in progress'
I thought I could do it with the following command, but for some reason I can't see the result of the org_users table
User.joins(:org_users).select("org_users.*, users.*")
result:
User.joins(:org_users).select("org_users.*, users.*")
User Load (0.6ms) SELECT org_users.*, users.* FROM `users` INNER JOIN `org_users` ON `org_users`.`user_id` = `users`.`id` LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "hoge", gender: 0, created_at: "2019-12-20 14:00:38", updated_at: "2019-12-20 14:00:38">]>
SELECT org_users., users. FROM users INNER JOIN org_users ON org_users.user_id = users.id LIMIT 11
If the generated query is directly hit to mysql, the information of org_users will appear
I don't know why the two results are different.
please let me know if there is anything i misunderstood.
My source code is at the following URL
https://github.com/negabaro/railsTestJoins
The model configuration is as follows.
app/models/org.rb
class Org < ApplicationRecord
has_many :org_users
has_many :users, through: :org_users
end
app/models/org_user.rb
class OrgUser < ApplicationRecord
belongs_to :org
belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
has_many :org_users
has_many :orgs, through: :org_users
end
users = User.joins(:org_users).select('users.id, users.name, users.gender, org_users.status')
Will do the trick. It will generate something like this:
#<User:0x00005649f318dc68 id: user_id, name: name ... and so on>
You can do users.first.attributes and it will show you desired hash:
{
id: id,
name: name,
gender: gender,
status: status
}

Rails 5: add/edit multiple records in "has_many through" relationship

I have classic has_many through relationship where I need to be able to add multiple Companies to particular User. Models look like this:
class Company < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :users, through: :accounts
end
class Account < ApplicationRecord
belongs_to :company, inverse_of: :accounts
belongs_to :user, inverse_of: :accounts
accepts_nested_attributes_for :company, :user
end
class User < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :companies, through: :accounts
end
In console I can add single record with this:
[1] pry(main)> user=User.find(7)
[2] pry(main)> user.accounts.create(company_id: 1)
How do I add, edit, delete multiple accounts for user in one query? I need to attach multiple Companies to User, then Edit / Remove if necessary.
So far I tried to implement array part from this tutorial, but somehow it does not work as obviously I'm doing something wrong here:
[4] pry(main)> user.accounts.create(company_id: [1,2])
(0.4ms) BEGIN
User Exists (1.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) AND ("users"."id" != $2) LIMIT $3 [["email", "tester#gmaill.com"], ["id", 7], ["LIMIT", 1]]
(0.6ms) COMMIT
=> #<Account:0x00000005b2c640 id: nil, company_id: nil, user_id: 7, created_at: nil, updated_at: nil>
As I understand I need to create array somehow and then operate with that. I would appreciate any help here. Thank you!
Solution
If anyone needs, I solved my problem a bit differently. I used checkboxes from this tutorial and it works just fine for me.
Here's an example with the bulk_insert gem:
company_ids = [1,2]
user_id = 1
Account.bulk_insert(
values: company_ids.map do |company_id|
{
user_id: user_id,
company_id: company_id,
created_at: Time.now,
updated_at: Time.now
}
end
)

How to set up this belongs_to association in Rails / ActiveRecord?

I have User and Review models. A review can have an author and a subject, both pointing to a User:
class Review < ApplicationRecord
belongs_to :subject, class_name: 'User', optional: true
belongs_to :author, class_name: 'User', optional: true
end
class CreateReviews < ActiveRecord::Migration[5.0]
def change
create_table :reviews do |t|
t.references :subject
t.references :author
end
end
end
This works fine and now I can assign two separate User objects to the Review object to represent who wrote the review against whom.
The user though, doesn't "know" how many reviews he's associated with either as a subject or the author. I added has_and_belongs_to_many :users on reviews and vice-versa, and though doable, isn't exactly what I want.
How do I set up the associations to be able to do the following:
review.author = some_other_user
review.subject = user2
another_review.author = some_other_user
another_review.subject = user2
user2.a_subject_in.count
#=> 2
user2.a_subject_in
#=> [#<Review>, #<Review>]
some_other_user.an_author_in.count
#=> 2
In other words, how do I see how many times a User has been saved as an author or subject for a model with belongs_to?
IF you want to use has_many association on users side, you need to define two separate has_many relations like
class User < ApplicationRecord
has_many :reviews, foreign_key: :author_id
has_many :subject_reviews, class_name: 'Review', foreign_key: :subject_id
end
Now with this you can simply use
irb(main):033:0> s.reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."author_id" = ? [["author_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, comment: "random", subject_id: 2, author_id: 1, created_at: "2016-07-12 01:16:23", updated_at: "2016-07-12 01:16:23">]>
irb(main):034:0> s.subject_reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."subject_id" = ? [["subject_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
Comment: subject_reviews is not a good name :), change it to your requirements.
I think you're looking for this query:
class User
def referenced_in
# this fetches you all the reviews that a user was referenced
Review.where("reviews.author_id = :user_id OR reviews.subject_id = :user_id", user_id: id).distinct
end
end
User.first.referenced_in #should give you all records a user was referenced

STI and preload relations

I have next models:
class User < ActiveRecord::Base
end
class Customer < User
has_many :orders, foreign_key: 'customer_id', dependent: :destroy
end
class Translator < User
has_many :orders, foreign_key: 'translator_id'
end
class Order < ActiveRecord::Base
belongs_to :customer, class_name: "Customer"
belongs_to :translator , class_name: "Translator"
end
And I have an issue when I try to load orders with translator and customer , like this:
Order.eager_load(:vox_logs,:customer, :translator)
In this case customer will be loaded fine but translator not:
order.translator = nil
order.translator_id = 123
How can I fix it?
EDIT:
I added console output for more clear (it's from a order's function):
(byebug) translator
nil
(byebug) translator_id
637
(byebug) Translator.find translator_id
Translator Load (70.1ms) SELECT "users".* FROM "users" WHERE ((users.roles_mask & 4) > 0) AND "users"."id" = $1 LIMIT 1 [["id", 637]]
<Translator id: 637, email: nil, first_name: nil, middle_name: nil,
last_name: nil, native_language: "fr", country: "RU", city: "Липецк"...

Rails ActiveRecord has_many through association query

When I query directly against my database I get the expected result, but not in rails. I'm guessing it has to do with my associations and something bad I said about Ruby 3 years ago.
Postgres SQL Query:
SELECT users.email, members.software, members.files
FROM users INNER JOIN members
ON members.user_id = users.id
WHERE members.region_id=2
Result: "dan#gmail.com";t;t "dan#test.com";t;t
BUT from rails c:
> ←[1m←[36mUser Load (1.0ms)←[0m ←[1mSELECT users.email,
> members.software, members.files FROM "users" INNER JOIN "members" ON
> "members"."user_id" = "users"."id" WHERE "members"."region_id" =
> 2←[0m> => #<ActiveRecord::Relation [#<User id: nil, email: "dan#gmail.com">, #<User id: nil, email: "dan#test.com">]>
That snippet was the resulting query from pasting in what I have tried to create in my controller and hard coding the region id:
User.joins(:members).select("users.email, members.software, members.files").where(members: {region_id: params[:id]})
These are my models:
class User < ActiveRecord::Base
has_many :members
has_many :regions, :through => :members
end
class Region < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :region
end
Is it the way I have associated my models or something else that I am missing?
Thanks!
What you are getting is active_relations object.
you can access the attributes like this
users = User.joins(:members).select("users.email, members.software as software, members.files as files").where(members: {region_id: params[:id]})
users.each do |u|
p u.email
p u.software
p u.files
end

Resources