I would like to select (do a join) to a record which has a column with boolean true and the table has_many records for this specific user.
For example, User model has_many PhoneNumbers and from the many PhoneNumbers he has a default phone number.
Is it possible to do it that way?
Thank you. :)
Let's say:
User(:id, :first_name, :last_name)
PhoneNumber(:id, :user_id, :value, :is_default)
where :is_default is the boolean field saying whether it's the default phone number of user or not. Then you can do it this way:
#users = User.joins(:phone_numbers).where('phone_numbers.is_default IS TRUE').select('users.*, phone_numbers.value AS phone_number')
Then you may do #users.first.phone_number to get the default phone number of the first user.
Hope this would help!
Related
I have 2 tables with millions of records:
First table is Subscription and second table is Message.
In Subscription table, I have 3 indexes:
subscription_id
msisdn
reporting_id
However, in Message table, I only have 2 indexes:
message_id
reporting_id
I have msisdn in Message table but it is not indexed.
So, I have a dashboard UI that let me search for Message.
I want to search Message using msisdn but since msisdn is not indexed, the query is taking forever!
but I want to put the idea of "Indexing msisdn in Message table" as plan B.
So my Plan A is(which is where I need help for):
Since both table has indexed reporting_id, I want to search for Message using subscription.msisdn in the dashboard, and then from there, I want to use the subscription.reporting_id and list all the Message with that subscription.reporting_id.
Something like subscription.reporting_id = message.reporting_id.
I have the view, controller and model for these 2 tables,
I tried doing this in both models
has_many :message, foreign_key: :reporting_id in Subscription.rb
and
belongs_to :subscription in Message.rb
but it didn't work.
In my view, the search_field for MSISDN is currently search for record from subscription.msisdn
Does anyone knows how can I achieve that?
PS: I want to prevent from editing the table structure.
If you want to establish a has_many :messages association in the Subscription class, you'll need to specify the primary_key.
class Subscription < ApplicationRecord
has_many :messages, foreign_key: "reporting_id", primary_key: "reporting_id"
end
With this, the SQL generated will look like this:
SELECT "messages".* FROM "messages" WHERE "messages"."reporting_id" = $1 [["reporting_id", "#{reporting_id}"]]
The primary_key ensures the reporting_id value of the Subscription model is used, else it will default to the id column.
However, rather than establishing this association which doesn't seem exactly normalized(atleast 3NF), you can just have a query to find all messages using the msisdn and reporting_id. Something like this should still be effective:
Message.
joins("INNER JOIN subscriptions ON subscriptions.reporting_id = messages.reporting_id").
merge(Subscription.where(msisdn: "#{value}"))
have to add primary key as well
has_many :message, foreign_key: :reporting_id, primary_key: :reporting_id in Subscription.rb
Found the solution!
In my Message.rb, I did:
belongs_to :subscription, foreign_key: :reporting_id, primary_key: :reporting_id
and it generated this query
SELECT `subscription`.* FROM `subscription` WHERE `subscription`.`reporting_id` = '1eaa7a61fe635005c81df347b1a7c401' LIMIT 1
My models: User, Group, and Membership. Users can have many groups through memberships, and vice-versa. I want to create a tool for website admins that produces a large table with the following specification:
Every row represents a user,
Every column represents a group,
In each cell of the table there is a boolean indicating whether the user belongs to the group.
What would be the best way to do this? Is it possible to write a single SQL query that achieves it (i.e. User.find_by_sql)? If not, how else?
p.s. I actually need a bit more than this (I need two columns per group, the first one indicating membership, and the second one counting how many meetings the user has attended in that group, but this involves the Meeting model, so I'll leave that for later.
Assuming that you're asking about the backend methodology not the data visualization aspect most of what JuanM. said is correct. One thing I would recommend is avoid writing his 'get_groups' method and just set up a 'has many through' relationship between users in groups. To do so put
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
end
In your Users model and vice versa in your Groups model (assuming memberships 'belongs_to' both). Then you'll have a '.groups' method on any User instance and a '.users' method on any Group instance
This would be my approach:
Write a function that returns if the user belongs to a group passed by parameter. Get all the groups from a user. In your user.rb model you can add this method get_groups to retrieve all groups from the user and then a method is_in(group). See code below:
class User < ActiveRecord::Base
#validations, some other stuff
has_many :memberships
#this method stores all the group-ids of the user in an array
def get_groups
myGroups = []
memberships.each do |membership|
membership.groups.each do |group|
myGroups << group.id
end
end
return myGroups
end
#this method receive a Group object and return true if the user belongs to that group
def is_in(group)
groups = get_groups
return groups.include?(group.id)
end
Then in your view or controller you can work as follow:
#the columns of the table
groups = Group.all
#iterating all users
User.all.each do |user|
#each user has to see if it belongs to each group in the groups table
groups.each do |group|
#the boolean value you display in a cell
value = user.is_in(group)
end
end
I am using SQLite3 and I would like the following to work:
class AddNameToGoal < ActiveRecord::Migration
def change
add_column :goals, :goal_name, :text, default: goal.exercise.name
end
end
Or maybe this makes more sense as what I'm trying to do:
add_column :goals, :gname, :text, default: Goal.find(row_id).exercise.name
How do I get the above to work.
I doubt it will work as it is but that's what I want.
Specifically, The user is associated with an Exercise through the exercise_id column.
belongs_to :user
belongs_to :exercise
has_many :workouts, dependent: :destroy
(This is the model for Goal)...
I would like the user to be able to choose their own name for the Goal but I can give them the hint to name the goal after the Exercise's name and if they choose to leave it blank it will default to the exercise's name. More importantly this must happen on the SQL side so that later when I have a collection drop down which requires a name of the goal they will need a name which corresponds to the exercise.
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
The Exercise Model is made in Rails to have
id, Name, other columns.
Exercise Model:
class Exercise < ActiveRecord::Base
has_one :goal
Is there a strategy by which that is possible.
Another option would be to help me find a strategy for active record so that I can do:
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
with (something else to replace goal_name with Exercise.name like goal.exercise.name and goal.id and show only the ID.
Doing this when you define the column on the table is problematic. I definitely think doing it upon creation, in the model, is how you'd want to go.
You might also check out the default_value_for gem and see if it helps.
Hi so in case any body ever wants to do this:
I figured out a strategy...
This is perfect for anything that has an instance of name in terms of the user.
For example, you have something that tracks your Car and you want to give that Car by default the name which comes from that Car's Make_and_Model (model) in Rails.
Naturally when someone says they have a new "Honda Accord" then they get to have that name, but if they ever want to change it to "Lucy" because her name is Lucy and you better treat her with the respect she deserves!, then this gives you the option to do that.
You do not want to change the name for that whole Make&Model you only want to change the name for that specific car.make_and_model which belongs_to User.
If you are wanting something to have a name that defaults to another name but allows the user to change that. Do that on the model level... by setting a before_save method inside the model... like so:
before_save :default_values
def default_values
self.goal_name = self.exercise.name if self.goal_name.nil?
end
I have the following schema:
Photos has many Groups has many Users.
I am using this Rails server as a backend to an iOS application and constantly need to push out notifications to all involved users of a group when a photo is added.
I problem is finding the least expensive query to resolve only the User.ids affected.
So far I have
Photo.find(1).groups.joins(:users)
I know that I have to put a select argument after this, but can't figure out the syntax for the life of me.
Given a photo, I am looking for the most efficient way to find a collection of the affected user id's.
Any help would be much appreciated!!!
In your Photo model, you can have another associations called users
has_many :group_users, :through => :groups, :source => :users
Then you can find the users by the following code
#photo = Photo.includes([:group_users]).where("photos.id = ?", 1).first
#affected_users = []
#photo.group_users.map {|user| #affected_users << user.id}
Now the #affected_users contains all the user ids.
users_id = []
Group.where(photo_id: 1).users.collect{|u| users_id << u.id}
puts users_id
As Photo has_many groups, so, groups belongs_to photo and groups table has photo_id as foreign key .
So, please try this.
I am working with a has_many through for the first time, and despite a lot of reading here and in the guide I am not understanding the correct way to access attributes on the through table. My tables are the same as this example from another post.
class Product < ActiveRecord::Base
has_many :collaborators
has_many :users, :through => :collaborators
end
class User < ActiveRecord::Base
has_many :collaborators
has_many :products, :through => :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :product
belongs_to :user
end
Assuming that the collaborators table has additional attributes, say hours_spent, what is the correct way to find the hours_spent from the collaborator table for a particular user and product?
When I have found my users via the product, and am iterating over them as in
#product.users.each do |user|
This seems to work
user.collaborator[0].hours_spent
I get the correct value, but since there should only be one collaborator record for each User/Product pair, the index is throwing me off, making me think I’m doing something wrong.
Thank you for reading!
EDIT
Perhaps I am not getting the has_many through concept. Maybe a MySQL example would help.
What I was thinking is that if I did
SELECT * FROM collaborators where user_id = 1;
I would expect a set (zero or more) as the result. Similarly
SELECT * FROM collaborators where product_id = 1;
would also give me a set, but
SELECT * FROM collaborators where user_id = 1 and product_id = 1;
would give at most 1 row.
If I am understanding properly, all 3 queries return a set. So I guess I need some kind of uniqueness constraint, but that would have to be a compound key of sorts, on both of the belongs to keys. Is that even possible? Is there a structure that better models this?
Thanks so much for the quick and helpful responses!
There may be a single database row per pair, but when considering a single user, that user can be associated to many products, so a user can have many rows in the collaborators table. Similarly, when considering a single product, that product can be associated to many users, so a product can have many rows in the collaborators table.
Also, instead of using user.collaborators[0].hours_spent, use user.collaborators.first.try(:hours_spent) (which may return null), if you only want the first collaborator's hours spent.
If a single user can only have one single product and a single product can only have a single user, then switch the has_many's to has_one's for everything.
Update: The preceding is the answer to the original question which has since been clarified via comments. See comments for detail and see comments on other answer by Peter.
Perhaps you should use has_and_belongs_to_many. If your Collaborator is used only to make link between User and Product without having more fields.
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
The beetween migration would be:
class CreateUsersProducts < ActiveRecord::Migration
def change
create_table "users_products", :id => false do |t|
t.integer :user_id
t.integer :product_id
end
end
end
After implementing this, what I found was that I think I had the correct relationships setup, I had to use the has_many :though as users could have many products, and it needed to be :through because there are additional attributes on the collaborator table. The sticking point was how to get there to only be a single Collaborator record for each user/product pair, and then how do I guarantee I got it. And to this point the answer I've found is it has to be done in code.
To make sure there is only a single record for each pair, I used
class Collaborator < ActiveRecord::Base
validates :product_id, :presence => true, :uniqueness => {:scope => [:user_id], :message => "This is a duplicate join"}
And then to make doubly sure I'm finding the right record, I have a scope
scope :collaboration_instance, lambda {|p_id, u_id| where("collaborations.product_id = ? && collaborations.user_id = ?", p_id, u_id)}
If someone has a more elegant solution, or just wants to improve this one, please post and I will change yours to the selected answer.