Rails and SQL straight request - sql

In rails we have Model.find(id_number) request. What I want to ask is there any possibility to create straight request to DB, for example: SELECT * FROM users WHERE id = 1 and replace rails basic Model.where(id: 1)?
EDIT
In my model.rb I have:
scope :search_import, -> {includes(:translations)}
default_scope -> {self.search_import}
And request (in rails console) looks like:
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User::Translation Load (0.6ms) SELECT `users_translations`.* FROM `users_translations` WHERE `users_translations`.`category_id` IN (1)
But I don't want to User::Translation was executed (with unscoped I have the same result)

Sure, something like
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = 1")

Related

Correct way to find models by their association

Both of these produce the same result:
User.where(account: 1)
User.where(account_id: 1)
But the generated SQL is different:
/* User.where(account: 1) */
SELECT "users".* FROM "users" WHERE "users"."account_id" = 1
/* User.where(account_id: 1) */
SELECT "users".* FROM "users" WHERE "users"."account_id" = $1 [["account_id", 1]]
Also both of these generate the same SQL as the first version:
a = Account.find(1)
User.where(account: a)
User.where(account_id: a)
# SELECT "users".* FROM "users" WHERE "users"."account_id" = 1
So which is the correct way to find a model by its association? Is the second version safer than the first? I tried to search for what's happening at the SQL level in the second version but I coudn't find anything.
There is no significant difference in your case. But if the account association is polymorphic, e.g. when there is a Business account and a Personal account, then the where(account: a) will generate something like WHERE account_type = 'Business' AND account_id = '123', while where(account_id: a) will generate just the WHERE account_id = '123'.

Check if a record is present in a has-many association

I have a rails-api application in which users can follow other users.
To check if an user already follows another user, I need to include a query in the attributes and because of that, I have always a N+1 query problem.
Here is my code:
Index action in user controller:
def index
#users = ::User.all.paginate(page: params[:page])
end
The followers will always be included by a default_scope in the User model.
index.json.jbuilder:
json.partial! 'attributes', collection: #users, as: :user
_attributes.json.jbuilder:
json.extract! user, :id, :firstname, :lastname, :username, :follower_count
is_follower = user.follower.find_by(id: current_user.id).present? if current_user
json.following is_follower
And as a result:
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 14]]
Rendered v1/user/users/_attributes.json.jbuilder (1.3ms)
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 9]]
Rendered v1/user/users/_attributes.json.jbuilder (1.4ms)
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 13]]
Is there some workaround or is it somehow possible to generate a dynamic attribute in the SQL query which includes the boolean value if the user follows the other user?
Thank you very much in advance.
My first thought would be to eager load the followers using the .includes method when you get the list of users like this #users = ::User.all.includes(:followers).paginate(page: params[:page]). But perhaps, I'm not understanding your question correctly? Let me know if that works or if I should focus my answer on a different subject. Thanks!
EDIT: Correct answer from the comments below:
Perhaps you can try user.followers.include?(current_user) to make use of the pre-loaded followers association.

SQL Injection in ActiveRecord

I'm trying to build a vulnerable demo application. I'm using SQLite, and I have ruby code that looks like this:
#value = current_user.accounts.calculate(:sum, params[:column])
And the SQL generates the following by default:
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) SELECT SUM("accounts"."account_value") AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
Next, I put ssn) FROM users WHERE name = 'Texas'; -- into the form and I get the following:
(0.3ms) SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?
Completed 500 Internal Server Error in 2ms
ActiveRecord::StatementInvalid (SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?):
app/controllers/instant_calculator_controller.rb:3:in `sum'
I think the issue is that the 'user_id' section tacked onto the end as a paramiterized query is messing this up. I tried doing something like ssn) FROM users WHERE name = 'Texas'OR user_id = ?; -- just to throw that part of the query away, but that didn't seem to help.
Does anyone have any thoughts on I could make this work? I can change the code as well as the query, but I'd prefer to change the query before changing to code to make it SQLiable.
EDIT:
A bit more info. If I take the SQL that is generated and just change the last user_id to '1' so it looks like SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --) AS sum_id FROM 'accounts' WHERE 'accounts'.'user_id' = 1 it works perfectly. I don't understand why this matters as everything after -- should be ignored.

Rails Rest Service GET

I have set up a Rails REST Service and I am having a problem showing a single record. Here is the URL that I am trying to hit:
http://website:3000/users/2/timesheets/21
Controller code:
def show
puts "SHOW"
puts params.inspect
#timesheets = User.find(params[:user_id]).timesheets(params[:id])
respond_to do |format|
format.json { render json: #timesheets }
end
end
I know the params are getting to the controller, but it is not using the :timesheet_id. Here is the console output to show what I mean:
Started GET "/users/2/timesheets/21" for **.**.***.** at 2013-03-19 06:12:11 -0400
Processing by TimesheetsController#show as */*
Parameters: {"user_id"=>"2", "id"=>"21"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "2"]]
Timesheet Load (0.5ms) SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."user_id" = 2
Completed 200 OK in 120ms (Views: 36.5ms | ActiveRecord: 2.9ms)
I see the timesheet id of 21 in the parameters hash. A query is then made to get the user, but then all of the timesheets for that user are grabbed. Any help would be appreciated. Thanks.
What Prakash suggests works, but executes two queries: one to get the user and one the get the timesheet. There does not seem to be any reason to do User.find(...) first. Might as well query the timesheets table only, which will only execute one query and is thus faster:
#timesheet = Timesheet.where('user_id = ? and id = ?', params[:user_id], params[:id]).first
Try this:
#user = User.find(params[:user_id])
#timesheet = #user.timesheets.find(params[:id])
This should run a query as follows:
SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."id" = 21 AND ("timesheets"."user_id" = 2) LIMIT 1
The corresponding view should be referring to #timesheet variable and not #timesheets.

Pass conditions to subqueries - rails activerecords

I have a QuestionCategory model who hasMany Question.
When I do this query #quizzs = QuestionsCategory.find(:all) everything is ok. It's querying all the catégories and associated questions:
QuestionsCategory Load (0.2ms) SELECT `questions_categories`.* FROM `questions_categories`
Question Load (0.3ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 1
Question Load (0.4ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 2
Question Load (0.3ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 3
But, I'd like to pass a condition to the questions subqueries. Something like
#quizzs = QuestionsCategory.find(:all, :questions => [ :conditions => {MY CONDITIONS} ])
And then get subqueries looking like this:
SELECT questions.* FROM questions WHERE questions.questions_category_id = 2 AND MY CONDITIONS
Possible?
Cheers mates
To get result for your SQL ( SELECT questions.* FROM questions WHERE questions.questions_category_id = 2 AND MY CONDITIONS) you can do as follows:
QuetionsCategory.find(2).questions.all(:conditions => [Your conditions])
How about a scope?
Question category controller:
def your_action_name
#quizzs = QuestionsCategory.all.collect{|qc| qc.questions.published == true}
Question model:
class Question < ActiveRecord::Base
scope :published, where(:published => true)
end
This way you can chain all the scopes (conditions) you'd like to apply to a Question
You Need Default scope In your model.
puts this In you QuestionCategory model.
default_scope joins(:questions).where(conditions)
Then
QuestionCategory.all Should give you expected result.