rails - find joining multiple fields under different names - ruby-on-rails

I have these two tables
tickets - id, user_id, manager_id etc
users - id, first_name etc
user_id and manager_id are both references for users
using rails is there a way of creating a find that pulls in the users info and managers(users) info.
The sql joins would look abit like this -
LEFT JOIN users ON users.id = tickets.user_id
LEFT JOIN users as managers ON managers.id = tickets.manager_id
Expected result
ticket:
subject: something interesting
user_id: 1
manager_id: 3
user:
id: 1
name: Frank
manager:
id: 3
name: Alex
Thanks, Alex

If I understood you correctly, you should use something like:
class Ticket < ActiveRecord::Base
belongs_to :user
belongs_to :manager, :class_name => "User"
end
class Ticket < ActiveRecord::Base
has_many :tickets
end
Then you can use
#ticket = Ticket.first
#ticket.user.name >> "Frank"
#ticket.manager.name >> "Alex"

Your models should be set up as this :
#ticket.rb
belongs_to :user
belongs_to :manager
#manager.rb
has_many :tickets
#user.rb
has_many :tickets
Examples of finds (still not sure how you want to set up your find ):
Ticket.find(x)
Ticket.all.select{|t|t.manager_id == #manager.id}
Ticket.find(:all, :conditions => 'manager_id="#manager.id"')

Related

Rails: foreign keys - how does the DB table looks like?

i have found this example here on stack overflow:
class User < ActiveRecord::Base
has_many :winners, class_name: "Competition", foreign_key: "competition_id"
end
class Competition < ActiveRecord::Base
belongs_to :winner, class_name: "User", foreign_key: "winner_id"
end
What exactly is the "has_many: winners" or "belongs_to: winner" in this case?
Is there a "competition_id" column in the users table and a "winner_id" column in the competitions table?
Greets
The tables would look something like:
users:
id: integer,
# other fields - name, email etc.
competitions:
id: integer,
winner_id: integer,
# other fields - name, place etc.
When someone uses the winners has_many association, for example like:
User.find(3).winners
it'll do a query that ends up as something like:
SELECT * FROM competitions WHERE winner_id = 3 # the 3 here comes from the user's id
When someone uses the winner belongs_to association, for example like:
Competition.find(4).winner
it'll do a query that ends up as something like:
SELECT * FROM users WHERE id = 1 # the 1 here has come from the winner_id on the competition record
The winners association is possibly poorly named; won_competitions would likely be a better name.
More reading: https://guides.rubyonrails.org/association_basics.html

active record - get related records from has_and_belongs_to_many and has_many

I am using ruby active record
class Blog < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :blogs
has_many :posts
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
end
How do I get Blog users who have atleast 1 Post or 1 comment
something like this in pseudo code
Blog.users.where(posts.count > 1 || comments.count > 1)
I believe you should be able to use the following, though haven't been able to test:
users = Blog.users
users.left_outer_joins(:posts).where.not(posts: { user_id: nil })
.or(
users.left_outer_joins(:comments).where.not(comments: { user_id: nil })
)
# or if you're on Rails < 5
query = <<-SQL
SELECT users.* from users
LEFT OUTER JOIN posts ON posts.user_id = users.id
LEFT OUTER JOIN comments ON comments.user_id = users.id
WHERE posts.user_id IS NOT NULL
OR comments.user_id IS NOT NULL
SQL
users.where(query)
In a nutshell, this insists there is a post and a comment with a user_id present in the query. This definitely works when checking for the existence of one association, though I've not tried it using or.
I've got an older version of Rails running at the moment, and would love to hear whether this does work as I'd expect it to, so if you could let me know how you get on #user2624242, that would be great.

Mutiple has_one of the same class

Got a really interesting situation with all that has_one and belongs_to relationships when Rails loads its dependant models in an inversed manner.
Let us have a model Couple with two related models of the same class, User:
class Couple < ActiveRecord::Base
has_one :male, class_name: "User"
has_one :female, class_name: "User"
end
class User < ActiveRecord::Base
belongs_to :couple
end
In this situation, when we create a Couple and assign it two instances of User, we will get into this:
# create all the stuff
couple = Couple.new
he = User.create name: 'Bob'
she = User.create name: 'Sara'
couple.male = he
couple.female = she
couple.save
# here's where the gap begins:
couple.male.name # => 'Bob'
couple.female.name # => 'Sara'
# Ok, so far so good...
Couple.find(couple.id).male.name # => 'Bob'
# What's the ..?!
Couple.find(couple.id).female.name # => 'Bob'
And what I've seen in the console performing all these, is this:
> couple.female.name
'Sara'
# nothing happens as the model is loaded already
> Couple.find(couple.id).female.name
SELECT `couples`.* FROM `couples` WHERE `couples`.`id` = 2 LIMIT 1
SELECT `users`.* FROM `users` WHERE `users`.`couple_id` = 2 LIMIT 1
'Bob'
# sure, here's the trouble!
Hmmm... That's not good... Searching over the Internet guided me to this: I created two classes, MaleUser and FemaleUser, both derived from User model. And changed the belongs_to :couple to a belongs_to :couple, foreign_key: :his_id and ... :her_id. Yet, the same result I seen on the screen.
My question is, why the hell this happens and how to perform that loading in a correct manner? So that Couple.find(couple_id).she would gave me the proper object?
UPD: tables structure:
create_table :users do |t|
t.integer :couple_id
# ...
end
create_table :couples do |t|
t.integer :his_id
t.integer :her_id
# ...
end
Thanks!
The relationship for the users in Couple needs to be a belongs_to relationship and not has_one. E.g:
class Couple < ActiveRecord::Base
# ...
belongs_to :male, :class_name => 'User', :foreign_key => 'his_id'
belongs_to :female, :class_name => 'User', :foreign_key => 'her_id'
end
This tells ActiveRecord that a Couple has two User object relations. One named male that can be retrieved with the ID found in the his_id column of the Couple table and one named female who's ID is found in the her_id column.
Whereas has_one would look for this relationship data on the Users table (which doesn't exist). The users table only references the couple_id and not whether the user is the male or female user for the Couple relationship.

Rails - association with another table

I have a users table(and a user model). In my scenario, a user can have multiple identities. I.e. user michael1 (id = 1) can be connected to michael2 (id = 2) and michael3 (id = 3).
I created a table to store these connections. I called it user_relations and it has: id, user_id, related_user_id columns. In the previous example I'll have:
user_id | related_user_id
1 | 2
1 | 3
In users model I defined: has_many :user_relations, and in user_relation I defined: belongs_to :users.
Now, I want that when I have a user object I would be able to get:
current_user.user_relations - and get all users objects that are connected according to the table. In previous example, if I have current_user as user with id 1, I would like to get users with id 2 and 3.
How can I achieve that?
BTW - I have an id because I saw that without it, I am not able to use destroy_all. If anyone has an insight regarding this also, I am open to hear.
I think this should work. If I missed something you can look here for details:
class User < ActiveRecord::Base
has_many :user_relations
has_many :related_users, :through=> :user_relations
end
class UserRelations< ActiveRecord::Base
belongs_to :user, :class_name => "User", :foreign_key => "user_id"
belongs_to :related_user, :class_name => "User", :foreign_key => "related_user_id"
end

Outer Join The Rails 3 Way

i have 3 models like :
location, user, discovered_location(location_id,user_id)
I think i need an outer join in order to get all locations, as well as include the discovered_location model, if that location has been discovered by the user.
I would need something like {location1, location2, location3:includes discovered_location, location4 ..}
Is there a Rails 3 way to do that ? If not, what is the best way ?
EDIT
I want to get the locations specified above, for a certain user. To better illustrate, it should be :
user {location1, location2, location3:includes discovered_location, location4 ..}
(A user has many discovered locations)
You can do an outer join in Rails only by using an SQL literal in the joins method:
joins("OUTER JOIN table2 on table2.column = table1.column")
joins makes an inner join, includes makes an outer join.
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations
You need to check that the user id in your discovered_locations table is either equal to the id of the user in question, or is null. This is easily accomplished with the meta_where gem. Given the following models:
class User < ActiveRecord::Base
has_many :discovered_locations
has_many :locations, :through => :discovered_locations
end
class Location < ActiveRecord::Base
has_many :discovered_locations
has_many :users, :through => :discovered_locations
end
class DiscoveredLocation < ActiveRecord::Base
belongs_to :user
belongs_to :location
end
Insert some dummy data, then execute a statement such as this:
Location.includes(:discovered_locations).where(
{:discovered_locations => {:user_id => User.first.id}} |
{:discovered_locations => {:user_id => nil}}
).each do |loc|
puts "#{loc.name} #{loc.discovered_locations.empty? ? 'not visited' : 'visited'}"
end

Resources