Uniqueness validation returning false non-uniqueness - sql

Adding states to an empty database, where State validates :name, uniqueness: true.
Again, the DB is empty!
State.create(name:"Alabama")
(0.2ms) BEGIN
State Exists (0.6ms) SELECT 1 AS one FROM "states" WHERE "states"."name" = $1 LIMIT $2 [["name", "Alabama"], ["LIMIT", 1]]
(0.3ms) ROLLBACK
=> #<State:0x00007ffb711c3c60 id: nil, name: "Alabama", region_id: nil, created_at: nil, updated_at: nil>
State.all
State Load (0.5ms) SELECT "states".* FROM "states"
=> []
Why does it say this state exists when NO states exist???
When I get rid of the uniqueness validation, it's still acting funny:
State.all
State Load (0.7ms) SELECT "states".* FROM "states"
=> []

Related

select vs distinct vs uniq?

I am confused by Ruby's ActiveRecord uniq method. I am using it to try to get back an array of complete objects, not just a particular field.
In my Padrino app script, which saves newspaper names and scores as Score objects, the uniq method by attribute on an ActiveRecord Relation is not working, and neither is distinct, with or without SQL syntax. Can anyone explain what is going on?
class Score < ActiveRecord::Base
def self.from_today
self.where('created_at > ?', Date.today)
end
end
scores = Score.from_today
scores.class
=> Score::ActiveRecord_Relation
scores.first
=> #<Score id: 123, score: -2.55, source: "Mail", created_at: "2016-08-11 04:29:24", updated_at: "2016-08-11 04:29:24">
scores.map(&:source)
=> ["Mail", "Guardian", "Telegraph", "Independent", "Express", "Mail"]
scores.uniq(:source).count
=> 6
scores.distinct(:source).count
=> 6
scores.select('distinct (SOURCE)').count
=> 5 #AHA, it works!
scores.select(:source).distinct
=> #<ActiveRecord::Relation [#<Score id: nil, source: "Telegraph">, #<Score id: nil, source: "Mail">, #<Score id: nil, source: "Independent">, #<Score id: nil, source: "Express">, #<Score id: nil, source: "Guardian">]>
#oops, no it doesn't
In Rails 5 distinct has no parameter. In Rails 4.2 the parameter is only true or false. When true, return distinct records, when false return none distinct records. uniq is in this case only an alias for distinct
So for Rails 4
scores.select(:source).distinct.count is what you want. This restricts distinct to column source

Rollback of sql query in Rails 4

I have a table:
create_table "requests", force: :cascade do |t|
t.string "name"
t.string "address"
t.integer "client_id"
t.string "phone"
t.string "mobile"
t.text "social_media"
t.string "email"
t.integer "type"
t.text "subtype"
t.text "description"
t.string "priority"
t.integer "responsible"
t.integer "price"
t.boolean "payed", default: false
t.datetime "date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
note, I have deleted all constraints, like not null etc.
and I have method:
def mark_payed
#request.update(payed: true)
redirect_to requests_path
end
so I want to update payed boolean attribute to true.
When i update request object with name defined like this one:(note: the name attribute is set)
2530;"ТОО ББ Нура";"---;--;-";;"---";"---";"";"";3;"";"---";"2";13;3000;FALSE;"";"2015-06-09 00:00:00";"2015-06-17 06:59:49.621709"
it works fine and my payed attribute is updated.
But in when i am updating this one: (note: the name attribute is empty ('').)
2532;"''";"''";89;"''";"''";"";"";3;"";"-";"2";2;1200;FALSE;"";"2015-06-09 00:00:00";"2015-06-16 11:18:54.296975"
I get a ROLLBACK.
HERE ARE THE LOGS FOR TWO CASES:
CASE 1:
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Request Load (0.4ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", 2533]]
Request Load (0.4ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", 2533]]
(0.4ms) BEGIN
(0.4ms) BEGIN
SQL (1.0ms) UPDATE "requests" SET "social_media" = $1, "subtype" = $2, "payed" = $3, "updated_at" = $4 WHERE "requests"."id" = $5 [["social_media", nil], ["subtype", nil], ["payed", "t"], ["updated_at", "2015-06-17 08:50:45.921870"], ["id", 2533]]
SQL (1.0ms) UPDATE "requests" SET "social_media" = $1, "subtype" = $2, "payed" = $3, "updated_at" = $4 WHERE "requests"."id" = $5 [["social_media", nil], ["subtype", nil], ["payed", "t"], ["updated_at", "2015-06-17 08:50:45.921870"], ["id", 2533]]
(8.7ms) COMMIT
(8.7ms) COMMIT
CASE 2:
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Request Load (0.4ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", 2532]]
Request Load (0.4ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", 2532]]
(0.2ms) BEGIN
(0.2ms) BEGIN
(0.4ms) ROLLBACK
(0.4ms) ROLLBACK
So i get rollback in the update query. Also if it makes sense I have an before_update: update_attributes which finds_by client_id.
Notice, that client_id is null in first case, which works,
and is set in second case which doesn't. So I concluded that it is not due to before_update.
Also it is kind of strange to see logs doubled, but it is out of scope of my question for now.
EDIT: my before_update action (btw, i commented out before_update, the problem is same)
def update_attributes
#client = Client.find_by(client_id: self.client_id) if self.client_id.present?
if #client.present?
filled_cartridges.each do |d|
#r = FilledCartridge.find_by(request_id: d.id)
#cartridge = Cartridge.find_by(cartridge_name: d.cartridge_name)
if #cartridge.nil?
#cartridge = Cartridge.create!(cartridge_name: d.cartridge_name)
end
d.client_id = #client.id
d.cartridge_id = #cartridge.id
end
end
# update repair_cartridges attributes
end

How to let the SQL results display in Rails's log?

I don't know whether this is a reasonable request. I hope to see the result in the log file such as log/development.log, Now the ActiveRecord::Base.logger.level = 0 and there is only the SQL statements:
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Excursion Load (0.3ms) SELECT "excursions".* FROM "excursions" WHERE "excursions"."id" = $1 LIMIT 1 [["id", "5"]]
Actor Load (0.5ms) SELECT "actors".* FROM "actors" WHERE "actors"."id" = 4 LIMIT 1
There is no results. I know in the 'rails console', when using ActiveRecord::Base.logger = Logger.new(STDOUT), I see the statements and corresponding results:
1.9.3-p551 :001 > ActiveRecord::Base.logger = Logger.new(STDOUT)
=> #<Logger:0x007f80441364d0 #progname=nil, #level=0, #default_formatter=#<Logger::Formatter:0x007f8044135c60 #datetime_format=nil>, #formatter=# <Logger::SimpleFormatter:0x007f8044134bd0 #datetime_format=nil>, #logdev=#<Logger::LogDevice:0x007f8044135440 #shift_size=nil, #shift_age=nil, #filename=nil, #dev=#<IO:<STDOUT>>, #mutex=# <Logger::LogDevice::LogDeviceMutex:0x007f8044135378 #mon_owner=nil, #mon_count=0, #mon_mutex=#<Mutex:0x007f8044134c98>>>>
1.9.3-p551 :002 > User.first
User Load (1.0ms) SELECT "users".* FROM "users" LIMIT 1
Actor Load (0.8ms) SELECT "actors".* FROM "actors" WHERE "actors"."id" = 2 LIMIT 1
ActivityObject Load (0.9ms) SELECT "activity_objects".* FROM "activity_objects" WHERE "activity_objects"."id" = 2 LIMIT 1
=> #<User id: 1, encrypted_password: "$2a$10$wFj.Q9XX0ua16BF0.SIps.VjnfVi8/6egicirc/SFxw3...", password_salt: nil, reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, authentication_token: nil, created_at: "2015-03-25 22:14:32", updated_at: "2015-03-25 22:14:32", actor_id: 2, language: nil, connected: false, status: "chat", chat_enabled: true, occupation: nil, invitation_token: nil, invitation_created_at: nil, invitation_sent_at: nil, invitation_accepted_at: nil, invitation_limit: nil, invited_by_id: nil, invited_by_type: nil>
What options I can set for this? Thanks in advance!
I researched some ActiveRecord (v3.2.21) code, It seems that there is no option about the SQL result. The above log User Load (1.0ms) SELECT "users".* FROM "users" LIMIT 1 is finished through callingActiveSupport::Notifications.instrument():
# query_cache.rb
def cache_sql(sql, binds)
result =
if #query_cache[sql].key?(binds)
ActiveSupport::Notifications.instrument("sql.active_record",
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
#query_cache[sql][binds]
else
#query_cache[sql][binds] = yield
end
result.collect { |row| row.dup }
end
So if I want to see the result in log I can add a log statement:
logger.info "#{result}"
result.collect { |row| row.dup }

Set ActiveRecord Scopes In A Loop

Why doesn't this work?
class Foo
...
Status.each do |status|
scope status, where(status: status)
end
...
end
Now Foo.new it returns not an instance of Foo but an ActiveRecord::Relation.
Try this in ruby 1.9
Status.each do |status|
scope status, -> { where(status: status) }
end
or in previous ruby versions
Status.each do |status|
scope status, lambda { where(status: status) }
end
-- EDIT --
I guess your problem is somewhere else, since this code is working for me:
class Agency < ActiveRecord::Base
attr_accessible :logo, :name
validate :name, presence: true, uniqueness: true
NAMES = %w(john matt david)
NAMES.each do |name|
scope name, -> { where(name: name) }
end
end
I can create new models just fine and use the scopes
irb(main):003:0> Agency.new
=> #<Agency id: nil, name: nil, logo: nil, created_at: nil, updated_at: nil>
irb(main):004:0> Agency.matt
Agency Load (0.5ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'matt'
=> []
irb(main):005:0> Agency.john
Agency Load (0.3ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'john'
=> []
irb(main):006:0> Agency.david
Agency Load (0.3ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'david'
=> []

How to get old value of column in a callback?

On after_update, in my model. How can I get the old value of a column?
changed_attributes() method of your model will get you a hash of changed attributes with their original values, even after_udpate. More info and more related methods here.
class MyModel < ActiveRecord::Base
after_update :log_changed
def log_changed
puts "changed attributes:"
puts changed_attributes.inspect
end
end
... gives the following in the console:
$ rails console
Loading development environment (Rails 3.0.7)
test(dev)> m = MyModel.first
=> #<MyModel id: 134611365, name: "oldname", created_at: "2011-09-16 10:27:53", updated_at: "2011-09-20 11:58:11">
test(dev)> m.name = 'newname'
=> "newname"
test(dev)> m.save
SQL (0.2ms) BEGIN
SQL (0.4ms) SHOW TABLES
AREL (0.2ms) UPDATE `mymodels` SET `updated_at` = '2011-09-20 12:07:34', `name` = 'newname' WHERE `mymodels`.`id` = 134611365
changed attributes:
{"name"=>"oldname", "updated_at"=>Tue, 20 Sep 2011 11:58:11 UTC +00:00}
SQL (83.9ms) COMMIT
=> true
test(dev)> m.changed_attributes
=> {}