Ruby on Rails: "SELECT" MySQL command - mysql

I'm writing an application using MySQL. There's a table called "Requests".
That table has a field "user_id" from Table "Users".
I'd like to select all requests from a user_id.
For example:
SELECT * FROM requests WHERE user_id = ("the id I want");
How can I do that using Ruby language, and not an SQL string?

In ActiveRecords its done like this:
Request.where(user_id: ID)

Request.find_by_user_id(USER-ID-YOU-WANT)
Or
Request.find_by user_id: 'USER-ID-YOU-WANT'
documentation here

There are a few different ways, as you can see from #shivam and #amalrik's answers. It also depends on the ORM you're using. If you're using Rails out of the box, you're probably using ActiveRecord.
Probably the most idiomatic way is to have the correct associations on your User and Request models.
in app/models/user.rb:
class User < ActiveRecord::Base
has_many :requests
end
and app/models/request.rb:
class Request < ActiveRecord::Base
belongs_to :user
end
This would allow you to find a user and call its #requests method:
User.find(user_id).requests

Related

Rails 5 HABTM from multiple databases works in mysql command but error in Rails

I am building an rails 5 app that connects to 2 different databases (dbA & dbB). My databases are on the same database host.
I want to make a wishlist. Pretty easy when using the same DB, but I am stuck with an "interesting" error.
This is what the databases look like:
the models are as follow:
user.rb
class User < ApplicationRecord
has_one :wishlist, dependent: :destroy
end
wishlist.rb
class Wishlist < ApplicationRecord
belongs_to :user
# has_and_belongs_to_many :wines
# The above did not work
# so I had to revert to has_many through
has_many :wines_wishlists
has_many :wines, through: :wines_wishlists
end
wines_wishlist.rb
class WinesWishlist < ApplicationRecord
belongs_to :wine
belongs_to :wishlist
def self.table_name_prefix
"dbA_#{Rails.env}."
end
# I added the above to prevent ActiveRecord from
# looking for the table in the wrong database
end
wine.rb (legacy model)
class Wine < ApplicationRecord
self.abstract_class = true
establish_connection LEGACY_DB
# LEGACY_DB is the legacy database connection info from a yaml file
# located in config.
def self.table_name_prefix
"dbB_#{Rails.env}."
end
end
This is quite straigth forward IMHO. Now the interresting error:
When I try the following :
user = User.last
user.wishlist.wines
It works on my local machine in development. It doesn't work on my staging server! When I try in the rails console, I get this:
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'dbA_staging.wines_wishlists' doesn't exist: SELECT `dbB_staging`.`wines`.* FROM `dbB_staging`.`wines` INNER JOIN `dbA_staging`.`wines_wishlists` ON `dbB_staging`.`wines`.`id` = `dbA_staging`.`wines_wishlists`.`wine_id` WHERE `dbA_staging`.`wines_wishlists`.`wishlist_id` = 1
This is the expected SQL.
user.wishlist.wines.to_sql
=> "SELECT `dbB_staging`.`wines`.* FROM `dbB_staging`.`wines` INNER JOIN `dbA_staging`.`wines_wishlists` ON `dbB_staging`.`wines`.`id` = `dbA_staging`.`wines_wishlists`.`wine_id` WHERE `dbA_staging`.`wines_wishlists`.`wishlist_id` = 1"
Even better, when I try the same SQL in rails db on my staging machine, it works!! It doesn't work in rails even though the SQL is correct, but it works in mysql command line.
I based my code on the following article and made some research, but I can't seem to figure out how to go around this problem.
I am using (same for development and staging):
Rails 5.1.1
ruby 2.4.0p0
mysql 5.6.34 (staging)
mysql 5.7.17 (development)
Any help would be greatly appreciated!
Taking a look at the article you linked to, it seems to be using a gem st-elsewhere, i.e.
has_many_elsewhere :wines, :through => :wines_wishlist
Also, as stated in the article, you can't make JOIN queries across database connections. The gem circumvents this using some less efficient queries, the details of which I did not look up.

Rails Arel joins with where condition on joined table

I'm trying to convert the following Rails where clause to use Arel, mostly to take advantage of the or method that Arel provides.
Post model
class Post
belongs_to :user
end
User model
class User
has_many :posts
end
I'm looking for posts posted by Mark.
This is the Rails Query:
Post.joins(:user).where(users: { first_name: 'Mark' })
I need to convert this query with Arel.
Thanks in advance!
This should do it.
# Generate Arel tables for both
posts = Arel::Table.new(:posts)
users = Arel::Table.new(:users)
# Make a join and add a where clause
posts.join(:users).on(posts[:user_id].eq(users[:id])).where(users[:first_name].eq('Mark'))
If you only need Arel for the where part (not for the join), I think this would be a better solution (will wield Activerecord results):
Post.joins(:user).where(User.arel_table[:first_name].eq('Mark'))

Rails 4 ActiveRecord - how to see how is interpreted a database query?

I have these models:
teacher
class Teacher < ActiveRecord::Base
has_many :days
end
day
class Day < ActiveRecord::Base
belongs_to :teacher
end
And running these query:
active_teachers = Teacher.joins(:days).where("teacher.id" => found_teachers.pluck(:teacher_id).uniq, "days.day_name" => selected_day)
What the query (should) does: found_teachers is an array of all teachers with duplications, remove the duplicity and chose only those teachers that have classes on a respective day (selected_day contains a string, Monday for example).
Because the amount of data in the variable active_teachers is so big that I can't manually go record by record (and I am not sure that I built this query properly and it does exactly what I need), I am trying to find out how is this query translated to SQL from ActiveRecord.
Usually I see everything in the terminal where is running server for the Rails app, but as of now, I don't see there this query stated.
So the question is, how can I see how the ActiveRecord query is translated to SQL?
Thank you in advance.
To get details from a query you're typing, you can do:
query.to_sql
query.explain
You can use
ActiveRecord::Base.logger = Logger.new STDOUT
and run your query in rails console. So it prints out the sql queries in the console

Rails: Selecting from multiple tables using .join()

Plugins Model:
class Plugin < ActiveRecord::Base
belongs_to :report
has_many :vulns
end
Vulns Model:
class Vuln < ActiveRecord::Base
belongs_to :plugins
end
I'm doing the following in rails:
#using * for now to select everything
#data = Plugin.select("*").joins(:vulns).where('plugins.id'=> plugin.plugin_id)
Which does the following query in the terminal:
SELECT * FROM `plugins` INNER JOIN `vulns` ON `vulns`.`plugin_id` = `plugins`.`id` WHERE `plugins`.`id` = 186
It's the right query but it doesn't select the content from the vulns table. I know it's the right query because I tried it in phpmyadmin and it returned the data on the vulns table too. When I do it in rails (using <%= debug(#data) %>) it only shows content from the plugins table.
How do I make it select everything from the vulns table too? (Each plugin has multiple vulns)
Apparently you can't have a column with the name "type".
If anybody has similar issues, rename the column called "type". I generated a new migration:
rails g migration RenameColumnOnVulnsTable
And then added the following:
def change
rename_column :vulns, :type, :vulnerability_type
end
The query works fine now.

Rails database query - is it possible to give a column an alias?

I've got to produce a json feed for an old mobile phone app and some of the labels need to be different from my database column names.
I think the most efficient way of doing this would be to do a create an alias at the database level. So I'm doing things like
Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
which produces the SQL
SELECT non_clashing_id AS clientID FROM `sites` WHERE `sites`.`mobile_visible` = 1 ORDER BY site_name
If I run this query in MYSQL workbench it produces a column with the heading ClientID as I expect, with the required values.
But if I show the object in a rails view I get {"clientID":null},{"clientID":null},{"clientID":null}
What am I doing wrong? Is there a better way of doing this?
This shows how to access the variable
sites = Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
sites.each do |site|
puts site.clientID
end
I think by default, activerecord loads column definitions from the database. And, it should load value into existing columns only.
Site.columns
I guess you could add one more item to that array. Or you could use the normal query without alias column name, then add alias_attribute like MurifoX did and overwrite as_json method:
class Site < ActiveRecord::Base
alias_attribute :client_id, :non_clashing_id
def as_json(options={})
options[:methods] = [:client_id]
options[:only] = [:client_id]
super
end
end
Try putting this in your model in addition to the database alias:
class model < ActiveRecord::Base
alias_attribute :non_clashing_id, :client_id
...
end