Rails console : query to database - ruby-on-rails

I have a model Report in which I defined those columns:
class Report < ActiveRecord::Base {
:id => :uuid,
:declarant_id => :uuid,
:reference => :integer,
:sent_at => :datetime,
:updated_by_id => :uuid,
:device_id => :string,
:created_at => :datetime,
:updated_at => :datetime,
:is_archived => :boolean,
:step_id => :uuid
}
and I want to filter the select query to get only the reports created 7 days ago, I defined this query related to some others inputs and methods :
r = Report.all.current.not_sent.only_finished_interventions.where("Report.created_at >= ?", 7.days.ago)
it not works !
when I do :
r = Report.all.current.not_sent.only_finished_interventions
I get the the requested reports.
can some one help to add the filter of 7.days.ago ?
I got this error in the rails c :
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing
FROM-clause entry for table "report" LINE 1: ... AND
"activities"."finished_at" IS NOT NULL) AND (Report.cre...

This should work:
.where("Reports.created_at >= ?", 7.days.ago)
Just make Report plural.
But I think Reports. is not necessary in your query bcs you don't join tables, so this should work too:
**`.where("created_at >= ?", 7.days.ago)`**

Related

Filter rails record by 2 instances of child record model

I'm trying to filter a list of Products based on 2 tags,
class Product < ActiveRecord::Base
has_many :tags
end
class Tag < ActiveRecord::Base {
:id => :integer,
:created_at => :datetime,
:updated_at => :datetime,
:key => :string
}
How can I format a query statement that allows me to find a product which has 2 tags, one with key 'fragile', and one with key 'perishable'?
Product.joins(:tags).where("tags.key IN (?)", ['fragile', 'perishable']).group('products.id').having('COUNT(tags.id) = ?', 2)

Rails 4 Migration

I had a table posts with column content of string type(255), when i migrate changes,to change type of string to text,it really change that type,but i get text(255) what does nothing.What im must to do to get such result :
TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT2 | :limit => 1 to 4294967296 (default = 65536)2
p.s. on my localmachine i can create posts of any long and type of string,but on heroku i get
PG::Error: ERROR: value is too long for type character variyng(255)
my _change_datatypes_on_posts_from_string_to_text.rb
class ChangeDatatypeOnPostsFromStringToText < ActiveRecord::Migration
def change
change_column :posts, :content, :text
change_column :posts, :title, :text
change_column :users, :name, :text
end
end
Try using the limit attribute...
eg : "change_column :posts, :content, :text, limit: nil".
On Postgres, a :string, limit:nil is effectively synonymous with :text.

Custom update lookup for accepts_nested_attributes_for

I have a mostly static table of devices like so:
class CreatePlatforms < ActiveRecord::Migration
def change
create_table :platforms do |t|
t.column :model, :string, :null => false
t.column :name, :string, :null => false
t.timestamps
end
add_index :platforms, :model, :unique => true
Platform.create :model => "iPhone1,1", :name => "Original iPhone"
Platform.create :model => "iPhone1,2", :name => "iPhone 3G"
[...]
end
end
And a table, devices, that references platforms. Now I would like to send the model to the server, and have the created device linked to the corresponding id of that model in the database, similar to accepts_nested_attributes_for :platform. However, that creates the record unless there is an id in the attributes.
Is there any way with accepts_nested_attributes_for, or something similar, to use a different attribute to look up existing records?
I could manually swap it out in the controller like the following, but that is quit messy and a last resort:
params[:device][:platform] = Platform.find_by_model params[:device][:platform_attributes][:model]
I found a solution:
def autosave_associated_records_for_platform
if new_platform = Platform.find_by_model(platform.model) then
self.platform = new_platform
else
self.platform.name = "Unknown"
self.platform.save!
end
end

Convert a SQL Query to Rails

At the moment, I am doing my complex queries by hand so to speak. But I keep encountering problems. For instance.
query = "SELECT histories.candidate_id
FROM histories
WHERE histories.institution_id IN (?)
GROUP BY histories.candidate_id
HAVING COUNT(*)= ?"
cand = [Code.find_by_sql([query,
params['searches'][key],
params['searches'][key].size])]
class History < ActiveRecord::Base
belongs_to :candidate
end
create_table "histories", :force => true do |t|
t.string "job_title"
t.date "start_date"
t.date "finish_date"
t.string "basic_salary"
t.string "bonus"
t.integer "institution_id"
t.integer "candidate_id"
t.datetime "created_at"
t.datetime "updated_at"
end
class Candidate < ActiveRecord::Base
# has_and_belongs_to_many :codes
has_many :codes, :through => :CandidatesCodes
has_many :histories
has_many :contacts
has_many :compensations
end
This returns a list of candidate ids.. but want I want it to return is a list of candidates how would I do that the rails way?
This is brians suggestion, and I have tried this but I get uninitialized constant History::Candidates
cand = History.find(:all,
:joins => :candidates,
:select => "candidates.*",
:conditions => [ "institution_id IN (?)", params['searches'][key] ],
:group => [ "candidate_id HAVING count(*) = ?", params['searches'][key].size ]
)
Try this:
Candidate.all(
:joins => :histories,
:conditions => {:histories=> {:institution_id => params[:searches][key]}},
:group => "candidates.id",
:having => "count(candidates.id) >= %i" % params[:searches][key].size
)
This should generate the following SQL:
SELECT candidates.*
FROM candidates AS candidates
JOIN histories AS histories ON histories.candidate_id = candidates.id
WHERE histories.institution_id IN (1,2,3)
GROUP BY candidates.id
HAVING COUNT(candidates.id) >= 3
Give this a try (building on Dinesh's approach above:
candidates = History.find(:all,
:joins => :candidates,
:select => "candidates.*"
:conditions => ["institution_id IN (?)", params['searches'][key]],
:group => ["candidate_id HAVING count(*) = ?", params['searches'][key].size]
)
Warning - untested.
Try this out.
Assuming that you have mode of History
History.find(:all ,
:conditions=>[" institution_id IN (? ) ",params['searches'][key] ],
:group => ["candidate_id HAVING count(*) = ? " ,params['searches'][key].size ]
)

Activerecord association question: getting has_many :through to work

I'm building an app in Ruby on Rails, and I'm including 3 of my models (and their migration scripts) to show what I'm trying to do, and what isn't working. Here's the rundown: I have users in my application that belong to teams, and each team can have multiple coaches. I want to be able to pull a list of the coaches that are applicable to a user.
For instance, User A could belong to teams T1 and T2. Teams T1 and T2 could have four different coaches each, and one coach in common. I'd like to be able to pull the list of coaches by simply saying:
u = User.find(1)
coaches = u.coaches
Here are my migration scripts, and the associations in my models. Am I doing something incorrectly in my design? Are my associations correct?
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :login, :string, :default => nil
t.column :firstname, :string, :default => nil
t.column :lastname, :string, :default => nil
t.column :password, :string, :default => nil
t.column :security_token, :string, :default => nil
t.column :token_expires, :datetime, :default => nil
t.column :legacy_password, :string, :default => nil
end
end
def self.down
drop_table :users
end
end
class CreateTeams < ActiveRecord::Migration
def self.up
create_table :teams do |t|
t.column :name, :string
end
end
def self.down
drop_table :teams
end
end
class TeamsUsers < ActiveRecord::Migration
def self.up
create_table :teams_users, :id => false do |t|
t.column :team_id, :integer
t.column :user_id, :integer
t.column :joined_date, :datetime
end
end
def self.down
drop_table :teams_users
end
end
Here are the models (not the entire file):
class User < ActiveRecord::Base
has_and_belongs_to_many :teams
has_many :coaches, :through => :teams
class Team < ActiveRecord::Base
has_many :coaches
has_and_belongs_to_many :users
class Coach < ActiveRecord::Base
belongs_to :teams
end
This is what happens when I try to pull the coaches:
u = User.find(1)
=> #<User id: 1, firstname: "Dan", lastname: "Wolchonok">
>> u.coaches
ActiveRecord::StatementInvalid: Mysql::Error: #42S22Unknown column 'teams.user_id' in 'where clause': SELECT `coaches`.* FROM `coaches` INNER JOIN teams ON coaches.team_id = teams.id WHERE ((`teams`.user_id = 1))
Here's the error in sql:
Mysql::Error: #42S22Unknown column 'teams.user_id' in 'where clause': SELECT coaches.* FROM coaches INNER JOIN teams ON coaches.team_id = teams.id WHERE ((teams.user_id = 1))
Am I missing something in my :through clause? Is my design totally off? Can someone point me in the right direction?
You can't do a has_many :through twice in a row. It'll tell you that its an invalid association. If you don't want to add finder_sql like above, you can add a method that mimics what you're trying to do.
def coaches
self.teams.collect do |team|
team.coaches
end.flatten.uniq
end
It's more of a many-to-many-to-even-more-relationship. I'd just write some sql:
has_many :coaches, :finder_sql => 'SELECT * from coaches, teams_users WHERE
coaches.team_id=teams_users.team_id
AND teams_users.user_id=#{id}'
I don't think ActiveRecord can handle doing a 2 step join in a has_many relationship. In order for this to work you'll have to join users to team_users to teams to coaches. The through option only allows for one extra join.
Instead you'll have to use the :finder_sql option and write out the full join clause yourself. Not the prettiest thing in the world, but that's how it goes with ActiveRecord when you try to do something out of the ordinary.
You could drop the "has_many :coaches, :through => :teams" line in users & then hand-write a coaches method in your User model like so:
def coaches
ret = []
teams.each do |t|
t.coaches.each do |c|
ret << c
end
end
ret.uniq
end
While I love to write SQL, I don't think it's the ideal solution in this instance. Here's what I ended up doing in the User model:
def coaches
self.teams.collect do |team|
team.coaches
end.flatten.uniq
end
def canCoach(coachee)
u = User.find(coachee)
coaches = u.coaches
c = []
coaches.collect do |coach|
c.push(coach.user_id)
end
return c.include?(self.id)
end
I thought about just doing it all in one fell swoop, but I liked the ability to return an array of coach objects from within the user object. If there's a better way to do it, I'm very interested in seeing the improved code.

Resources