How to write the active record query - sql

I have three models in context of this question:
class ClearanceBatch < ActiveRecord::Base
has_many :items
belongs_to :user
end
class Item < ActiveRecord::Base
belongs_to :style
belongs_to :user
belongs_to :clearance_batch
validates :id, :uniqueness => true
end
class User < ActiveRecord::Base
has_many :items, dependent: :destroy
has_many :clearance_batches, dependent: :destroy
enum role: {staff: 0, vendor: 1, admin: 2}
end
Schema:
create_table "clearance_batches", force: :cascade do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "status", default: false
t.string "boughtby", default: ""
t.integer "user_id"
end
add_index "clearance_batches", ["user_id"], name: "index_clearance_batches_on_user_id"
create_table "items", force: :cascade do |t|
t.string "size"
t.string "color"
t.string "status"
t.decimal "price_sold"
t.datetime "sold_at"
t.integer "style_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "clearance_batch_id"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "role", default: 0
end
I want to find all items in a batch of the currently logged in user(mainly vendor) with the status "clearanced", and get their details in a loop from controller to my view
Can anyone please help me out with the active record query? Please! :)
The SQLite Query I think would be:
Select I.id from clearance_batches C INNER JOINS Items I on C.id = I.clearance_batch_id where C.user_id = "1" and I.status = "clearanced"
(If 1 is the current user, keeping in mind I am only allowing user of role vendors to be a user in clearance_batch table)

(1) Query:
Items.where(status: "clearanced")
.joins(:clearance_batches)
.where(clearance_batches: {user_id: current_user})
(2) Controller:
#clearanced_items = query(1)
(3) View:
<% #clearanced_items.each do |c_item| %>
...
<% end %>

Related

Association command using has_many :through associations in Ruby on Rails

I have a has_many :through association.
#app/user.rb
class User < ApplicationRecord
has_many :members
has_many :projects, :through => :members
end
#app/project.rb
class Project < ApplicationRecord
has_many :members
has_many :users, :through => :members
end
#app/member.rb
class Member < ApplicationRecord
belongs_to :user
belongs_to :project
end
I have the database schema as follows:
create_table "members", force: :cascade do |t|
t.integer "user_id"
t.integer "project_id"
t.integer "is_owner"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["project_id"], name: "index_members_on_project_id"
t.index ["user_id"], name: "index_members_on_user_id"
end
create_table "projects", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I can get Member ID, User_ID, Project_ID, and is_owner when I use the command #project.members
I can get user_id, first_name, last_name, email, and password when I use the command #project.users
What command should I use to get member_id, first_name, last_name?
I can get what I want using the SQL query SELECT * FROM members INNER JOIN users ON users.id = members.user_id but I don't want to use raw SQL.
Can someone tell me how to convert that query into a Ruby on rails command?
You can get your desired result using following code
Member.joins(:user)
It will generate the same query what you are specifying in your question i.e.
SELECT * FROM members INNER JOIN users ON users.id = members.user_id

How do I write an ActiveRecord query that filters out results from a joined table?

Users and Sessions are joined by a has_and_belongs_to_many association.
How do I get the unique list of Users that meet the following conditions?
user.coach == true
user.available == true
And then NOT include a user if that user is a coach in any active Session:
session.coach_id == user.id
session.call_ends_at == nil
Is there a way I can write this with ActiveRecord Query language? Do I need to write a pure SQL statement? Some kind of hybrid? What would YOU do?
I also have scopes defined that could be helpful here. But I'm not sure how to add them in:
User.available_coaches (scope)
Session.in_progress (scope)
User model
class User < ActiveRecord::Base
has_many :client_sessions, class_name: 'Session', foreign_key: :client_id
has_many :coach_sessions, class_name: 'Session', foreign_key: :coach_id
scope :coaches, -> { where(coach: true) }
scope :available_coaches, -> { coaches.where(available: true) }
Session model
class Session < ActiveRecord::Base
belongs_to :client, class_name: 'User'
belongs_to :coach, class_name: 'User'
scope :in_progress, -> { where.not(coach: nil).where(call_ends_at: nil) }
Schema
create_table "sessions", force: :cascade do |t|
t.integer "client_id"
t.integer "coach_id"
t.boolean "canceled", default: false
t.datetime "coach_accepted_at"
t.datetime "call_begins_at"
t.datetime "call_ends_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "sessions", ["client_id"], name: "index_sessions_on_client_id", using: :btree
add_index "sessions", ["coach_id"], name: "index_sessions_on_coach_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.boolean "coach", default: false
t.boolean "available", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I would do it with SQL exists:
User.where(coach: true, available: true).
where("not exists (select 1 from sessions " +
"where sessions.coach_id = users.id and sessions.call_ends_at is null)")
Note that since there is no join to sessions there is no need for .uniq.

How can i use data from antoher tables in a active record where in rails

im trying to make a search form in a table that contains two ids as values, one of them is the id of an user and the other is the id of a project.
Here is my database schema:
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.text "bio"
t.float "lastLatitude"
t.float "lastLongitude"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "swipes"
t.string "profile_image"
end
create_table "projects", force: :cascade do |t|
t.string "name"
t.text "bio"
t.integer "user_id"
t.string "salary"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "likes"
t.string "image"
t.float "latitude"
t.float "longitude"
end
create_table "matches", force: :cascade do |t|
t.integer "user_id"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
What i want to do is to create a search form in the matches index view to filter the matches by the user name or the project name, but i don't know how to do it, this is what i have right now:
def self.search(search)
if search
where('User(user_id).name LIKE :search OR Project.name LIKE :search', search: "%#{search}%")
else
where(nil)
end
end
Is it possible to do that?
Any help is welcome
I suggest a scope approach
class Match < ActiveRecord::Base
belongs_to :project
belongs_to :user
scope name, ->(n) {
joins(:user, :project).where("users.name like ? or projects.name like ? ", "%#{n}%", "%#{n}%")
}
end
class User < ActiveRecord::Base
has_many :projects
scope :name, ->(n) {
where("users.name like ?", "%n%")
}
end
class Project < ActiveRecord::Base
scope :name, ->(n) {
where("projects.name like ?", "%n%")
}
end

Testing with rspec is causing an error: `Migrations are pending. To resolve this issue, run: bin/rake db:migrate RAILS_ENV=development `

when I run the rails server, localhost displays this error:
ActiveRecord::PendingMigrationError (Migrations are pending.
To resolve this issue, run: bin/rake db:migrate RAILS_ENV=development):
I have run the bin/rake... and the next error says:
$ bundle exec bin/rake db:migrate RAILS_ENV=development
== 20150225172130 CreateVotes:
migrating ======================================
-- create_table(:votes)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: table "votes" already exists: CREATE TABLE "votes"
("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "value" varchar(255),
"user_id" integer, "post_id" integer, "created_at" datetime, "updated_at"
datetime)
_create_votes.rb
class CreateVotes < ActiveRecord::Migration
def change
create_table :votes do |t|
t.string :value
t.references :user, index: true
t.references :post, index: true
t.timestamps
end
end
end
Basically, it tells me I have migrations pending, but when I attempt to migrate it says the table already exists.
20150225172130 CreateVotes:
migrating ======================================
-- create_table(:votes)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: table "votes" already exists: CREATE TABLE "votes"...
after running db:drop:all
db:create
db:migrate
db:test:clone
votes are now showing up in schema.rb:
ActiveRecord::Schema.define(version: 20150101200224) do
create_table "comments", force: true do |t|
t.text "body"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id"
create_table "posts", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name:"index_users_on_reset_password_token", unique: true
create_table "votes", force: true do |t|
t.integer "value"
t.integer "user_id"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "votes", ["post_id"], name: "index_votes_on_post_id"
add_index "votes", ["user_id"], name: "index_votes_on_user_id"
end
model/vote.rb
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
You could do this:
class CreateVotes < ActiveRecord::Migration
def change
unless table_exists? :votes
create_table :votes do |t|
t.string :value
t.references :user, index: true
t.references :post, index: true
t.timestamps
end
end
end
I've run into this before when a migration didn't run as expected. But you should also check your schema.rb file to make sure that the votes table has the columns that the migration adds.

Activerecord query not returning all results

I've been having a problem with an ActiveRecord query in Rails 4:
my models:
class Addressee < ActiveRecord::Base
has_and_belongs_to_many :emails
end
class Email < ActiveRecord::Base
belongs_to :author, foreign_key: :from_id, class_name: "Addressee"
has_and_belongs_to_many :addressees
end
my schema.rb
create_table "addressees", force: true do |t|
t.string "token", limit: nil
t.string "domain", limit: nil
t.string "email", limit: nil
t.string "name", limit: nil
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "addressees_emails", force: true do |t|
t.integer "addressee_id"
t.integer "email_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "emails", force: true do |t|
t.string "message_id", limit: nil
t.integer "from_id"
t.string "subject", limit: nil
t.text "body"
t.datetime "date"
t.datetime "created_at"
t.datetime "updated_at"
end
my query:
# Returns correct emails
query_params = { "addressees.email" => "test#example.com" }
#emails = Email.includes(:addressees).where(query_params).references(:addressees)
my problem:
# Returns only the addressee matching the email from query params
#emails.last.addressees
#<ActiveRecord::Associations::CollectionProxy [#<Addressee id: 12, token: "test", domain: "example.com", email: "test#example.com", name: nil, created_at: "2014-09-25 14:06:34", updated_at: "2014-09-25 14:06:34">]>
#Returns the wrong count
#emails.last.addressees.size
#=> 1
#Returns the correct count (because it does a new query)
#emails.last.addressees.count
#=> 3
my question:
How can I modify the query to include all addressees without the need to do another query?. I'm passing the #emails var to my json serializer and now it only includes 1 addressee instead of all 3.
#emails = Email.where('emails.id IN SELECT(email_id FROM addressees_emails WHERE addressee_id IN (SELECT id FROM addressees WHERE email IN (?)))', %w[test#example.com test2#example.com]).includes(:addressees)