rails new example --database=postgresql
rails g model Thing uid flag:boolean
self.primary_key = 'uid'
a app/models/thing.rb
uid
e tente atualizá-lo.Após a atualização, espero que o valor tenha sido atualizado com sucesso.
Usando rails console
, eu pego uma coisa que criei e tento atualizá-la. A atualização parece ter sido bem-sucedida, mas assim que recarrego minha instância do Thing, fica claro que a atualização não funcionou.
2.4.0 :002 > t = Thing.first
Thing Load (0.4ms) SELECT "things".* FROM "things" ORDER BY "things"."uid" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Thing id: "HI", uid: "HI", flag: nil, created_at: "2017-06-03 17:15:06", updated_at: "2017-06-03 17:15:06">
2.4.0 :003 > t.update(flag: true)
(0.3ms) BEGIN
SQL (1.1ms) UPDATE "things" SET "flag" = $1, "updated_at" = $2 WHERE "things"."uid" = $3 [["flag", "t"], ["updated_at", "2017-06-03 17:28:35.485354"], ["uid", "1"]]
(0.4ms) COMMIT
=> true
2.4.0 :004 > t
=> #<Thing id: "HI", uid: "HI", flag: true, created_at: "2017-06-03 17:15:06", updated_at: "2017-06-03 17:28:35">
2.4.0 :005 > t.reload
Thing Load (0.5ms) SELECT "things".* FROM "things" WHERE "things"."uid" = $1 LIMIT $2 [["uid", "HI"], ["LIMIT", 1]]
=> #<Thing id: "HI", uid: "HI", flag: nil, created_at: "2017-06-03 17:15:06", updated_at: "2017-06-03 17:15:06">
A propriedade flag
retorna para nil
após recarregar. Você pode ver que a chamada para atualizar está tentando encontrar o registro w/ ["uid", "1"]
, que é o id serial original, e não o campo uid especificado.
Isso funcionou antes de atualizar para o Rails 5 :-(
Além disso, se eu tentar validar uniqueness
em :uid
, não consigo nem save
registros existentes. O ActiveRecord acaba encontrando o mesmo registro no banco de dados e não percebe que eles são iguais. No entanto, acredito que isso decorre do mesmo problema de misturar o primary_key
(que foi definido como uid
) e o padrão id
.
Versão do Rails : 5.1.1
Versão do Ruby : 2.4.0
Estou tendo o mesmo problema. Querendo saber se você encontrou uma solução alternativa. O id
é um número inteiro, mas o primary_key
é uma string e está tentando usar o id
como primary_key
para fazer referência ao registro para atualizações . Modelo:
# Table name: people
#
# id :integer not null
# orn :string not null, primary key
# notes :text
#
class Person < ActiveRecord::Base
self.primary_key = :orn
attr_readonly :orn
> person = Person.find 'ORN00008079837'
Person Load (0.2ms) SELECT "people".* FROM "people" WHERE "people"."orn" = $1 LIMIT $2 [["orn", "ORN00008079837"], ["LIMIT", 1]]
> person.update! notes: nil
(0.1ms) BEGIN
SQL (0.4ms) UPDATE "people" SET "updated_at" = $1, "notes" = $2 WHERE "people"."orn" = $3 [["updated_at", "2017-06-06 21:41:13.814079"], ["notes", nil], ["orn", "8079849"]]
(1.4ms) COMMIT
=> true
então o Rails acha que deu certo, mas o banco de dados não foi atualizado, pois a cláusula WHERE
não corresponde a nenhum registro. no banco de dados id
é de fato 8079849
mas no Rails id
é ORN00008079837
...
eu encontrei uma solução alternativa para atualizações. talvez ajude a entender o problema subjacente:
def id_in_database
self[self.class.primary_key]
end
eu estava olhando para a seguinte fonte e adivinhando o que acontece a seguir:
activerecord-5.1.0/lib/active_record/persistence.rb:575
Isso funcionou antes de atualizar para o Rails 5 :-(
Fiquei intrigado com isso, pois a correção proposta (https://github.com/rails/rails/pull/29378) altera o código que existe há muito mais tempo do que isso. Transformei o novo teste daquele PR em um script de reprodução e dividi a falha.
Começou a falhar em https://github.com/rails/rails/commit/16ae3db5a5c6a08383b974ae6c96faac5b4a3c81 , quando id_was
foi substituído por id_in_database
aqui . Verificar esse commit e reverter essa linha fez o teste passar.
No entanto, fazer a mesma alteração no master não corrigiu o teste. Eu o rastreei para https://github.com/rails/rails/commit/b5eb3215a68f94bb8cb20739366232c415744b83 , que alterou o método id_was
para usar _read_attribute
em vez de passar pelo id
e pegando a chave primária.
Para resumir: id_was
costumava ler sempre a chave primária, id_in_database
foi introduzido e sempre lia a coluna id
, e então id_was
foi alterado para se comportar como id_in_database
. https://github.com/rails/rails/pull/29378 faz com que ambos os métodos funcionem como id_was
antes do 5.1.
Comentários muito úteis
Fiquei intrigado com isso, pois a correção proposta (https://github.com/rails/rails/pull/29378) altera o código que existe há muito mais tempo do que isso. Transformei o novo teste daquele PR em um script de reprodução e dividi a falha.
Começou a falhar em https://github.com/rails/rails/commit/16ae3db5a5c6a08383b974ae6c96faac5b4a3c81 , quando
id_was
foi substituído porid_in_database
aqui . Verificar esse commit e reverter essa linha fez o teste passar.No entanto, fazer a mesma alteração no master não corrigiu o teste. Eu o rastreei para https://github.com/rails/rails/commit/b5eb3215a68f94bb8cb20739366232c415744b83 , que alterou o método
id_was
para usar_read_attribute
em vez de passar peloid
e pegando a chave primária.Para resumir:
id_was
costumava ler sempre a chave primária,id_in_database
foi introduzido e sempre lia a colunaid
, e entãoid_was
foi alterado para se comportar comoid_in_database
. https://github.com/rails/rails/pull/29378 faz com que ambos os métodos funcionem comoid_was
antes do 5.1.