I've got a bit of code that I'm looking at the moment :
User.includes([:profile => :schedule]).where('...')
This loads all profiles and their schedules. So the query produced by this is select * user fields, select * profile fields, select * schedule fields
I'm filtering out users based on one field from profile and based on one field from profiles schedule but this above statement is selecting all the fields from all of these.
So I tried to do it with joins :
User.joins(:profile).joins('profiles.schedule').where('....').select('....')
This is throwing out error. I'm a former Java developer, still learning the rails stuff.
This should work:
User.joins(profile: :schedule).where('....').select('....')
If you've set your Profile model to have the association Schedule using the has_many through association then you can just use this:
User.joins(:schedule).where('...')
Try this:
User.joins(profile: [:schedule]).where('...')
or
User.joins(profile: :schedule).where('...')
Related
I'm working on my first (kinda) big personal project and I am stuck. I have 4 tables, 3 of which have foreign keys linking into tbl_model_details. All tables are listed below.
tbl_model_details
tbl_model_type
tbl_model_name
tbl_model_scale
Ideally I want to show data through my controller with HTTP Get. I can get Postman to to return data from my controller using _context.tbl_model_details.ToList();
Currently Postman is showing the id's for the other tables, but want them to show data from other columns within those tables instead of the id.
Within SQL I was able to build this query which displays the information I would like from the other tables, Is there an equivalent that I could make to run inside my controller? Or is there a way I can use this query that I have already made?
SELECT model_scale, model_name, info, picture, model_type, part_number, amount_owned, modified, limited_addition, date_purchase, price_paid, upc
from tbl_model_details
join tbl_model_type
on tbl_model_details.type_id = tbl_model_type.type_id
join tbl_model_name
on tbl_model_details.name_id = tbl_model_name.name_id
join tbl_model_scale
on tbl_model_details.scale_id = tbl_model_scale.scale_id
Any help from you guys would be great.
Thanks
You can use Entity Frameworks LINQ Include. This will allow you to include the sub-models in the same query:
_context.tbl_model_details
.Include(details => details.tbl_model_type)
.Include(details => details.tbl_model_name)
.ToList();
Without knowing your relationships, DBSet and Model setups, I can say that the statement will look exactly like the one I mentioned, but this may help you get on the right track.
This will allow you to later retrieve data from the sub-models:
#Model.tbl_model_scale.model_scale;
I have what I suspect is a basic question. In my rails app, One user has many scores. Is there a way to get all the scores of an active relation of users. I can do user.scores (obviously) but if users is a group of users I need to do something like users.scores. This is clearly not correct.
You can use
Score.where(user: users)
This will construct sql like
select * from scores where user_id in (select *.id from users where ..)
Did you try the following?
#users.collect(&:scores).flatten!
I'm new to Rails and I would like to know if I can check if data already exists in my database while uploading new data from CSV. So far I haven't found this on the net.
I use a Postgres database. I don't know if I have to check it in Rails or in Postgres. In my database there are some columns like id, personal_id, cost_center. So I will check if one (or more) of my new data has the same personal_id with the the same cost_center while uploading.
How can I do this?
UPDATE
I've tried the solution of #huan son, it works but not the way I need it. So I tried different things and I think a SQL query is in my case the best choice.
DELETE FROM bookings WHERE id NOT IN (SELECT MIN(id) FROM bookings GROUP BY personal_id, wbs, date, hours, cost_center)
Booking.delete_all.where('id NOT IN (?)', Booking.select('MIN(id)').group(:personal_id, :wbs, :date, :hours, :cost_center).map(&:id))
My SQL query works like the way I want it but I don't know the right "translation" into rails because with the second code above my whole bookings table gets deleted
Solution
The solution for my problem is:
Booking.delete_all(['id NOT IN (?)', Booking.group(:personal_id, :wbs, :date, :hours, :cost_center).pluck('MIN(id)')])
Those are unique scopes.
You need to define those first in your database migration so postgres is making sure there is no double-value wie [key, key1, key2...]
add_index :table, [:personal_id, :cost_center_id], :unique => true
then you need to go into your rails-model and catch that uniqueness at validations.
validates_uniqueness_of :personal_id, :scope => :cost_center_id
with that, rails is querying every time before creating a object, the database and check if something with the unique-pair-values already exists. if so, its adding it to the #errors of the model, so the model can't be saved
You can use uniqueness validation, but in my experience when importing data from CSV, the problem is that if the item already exists, all the validation does is stop the record being saved. In most examples, you usually want to do something with the matched record. Therefore, I'd suggest you also look at find_or_initialize_by. This allows you to also update existing records from imported data.
So if you have Thing model with a name and cost for example, you may want to identify existing things by name, and update their costs. And create new things where no matching name exists. The following code would do that:
name, price = some_method_that_gets_name_and_price_from_csv
thing = Thing.find_or_initialize_by name: name
thing.price = price
thing.save
Also have a look at find_or_create_by which can be more suitable in some situations. I'd also still keep the validation of uniqueness in the model. I just wouldn't use validation to handle how the data was imported.
My models have a many-to-many relationship where Coach could have coached many Teams, and a Team could have multiple coaches.(Assistant, Head, etc)
in Rails Console, when I run:
#coach = Coach.joins(:teams).select("coaches.first_name, coaches.last_name, teams.team_level")
returns:
=> [#<Coach first_name: "john", last_name: "doe">]
notice that it doesnt return the teams.team_level, so I cant use #coach.team_level on my view
When I do .to_sql it returns:
=> "SELECT coaches.first_name, coaches.last_name, teams.team_level
FROM `coaches`
INNER JOIN `coach_teams` ON `coach_teams`.`coach_id` = `coaches`.`id`
INNER JOIN `teams` ON `teams`.`id` = `coach_teams`.`team_id`
Which is what I expect... So when I run this Query against my DB, I get the expected fields.
What am I doing wrong here/what am I not seeing? Thanks for looking into this!
You're not doing anything wrong, you use a method of the Coach model so you get Coach model/s.
Since you're using a join on teams you can access the team_level value without an additional query.
It is actually good. You ask for a Coach model, you join the Team model with it.
So you actually just need #coach.teams.team_level to access the team level.
The select can't change the schema of your modell, it can only filter down the attributes returned form the SQL, this way you need less roundtrip and less data transfer, but the structure is unchanged.
In my application Users register for Events, which belong to a Stream. The registrations are managed in the Registration model, which have a boolean field called 'attended'.
I'm trying to generate a leaderboard and need to know: the total number of registrations for each user, as well as a count for user registrations in each individual event stream.
I'm trying this (in User.rb):
# returns an array of users and their attendence count
def self.attendance_counts
User.all(
:select => "users.*, sum(attended) as attendance_count",
:joins => 'left join `registrations` ON registrations.user_id = users.id',
:group => 'registrations.user_id',
:order => 'attendance_count DESC'
)
end
The generated SQL works for just returning the total attended count for each user when I run it in the database, but all that gets returned is the User record in Rails.
I'm about to give up and hardcode a counter_cache for each stream (they are fairly fixed) into the User table, which gets manually updated whenever the attended attribute changes on a Registration model save.
Still, I'm really curious as to how to perform a query like this. It must come up all the time when calculating statistics and reports on records with relationships.
Your time and consideration is much appreciated. Thanks in advance.
Firstly as a couple of points on style and rails functions to help you with building DB queries.
1) You're better writing this as a scope rather than a method i.e.
scope attendance_counts, select("users.*, sum(attended) as attendance_count").joins(:registrations).group('registrations.user_id').order('attendance_count DESC')
2) It's better not to call all/find/first on the query you've built up until you actually need it (i.e. in the controller or view). That way if you decide to implement action / fragment caching later on the DB query won't get called if the cached action / fragment is served to the user.
3) Rails has a series of functions to help with aggregating db data. for example if you only wanted a user's id and the sum of attended you could use something like the following code:
Registrations.group(:user_id).sum(:attended)
Other functions include count, avg, minimum, maximum
Finally in answer to your question, rails will create an attribute for you to access the value of any custom fields you have in the select part of your query. e.g.
#users = User.attendance_counts
#users[0].attendance_count # The attendance count for the first user returned by the query