我挣扎着用自定义主键将我的表建模为Rails模型和id
作为正则(非空)列。
问题是我的表有:
auto_id
主键列AUTO_INCREMENTid
为自定义列,NOT NULL
约束(保存前由应用端设置)
class InitialMigration < ActiveRecord::Migration[6.1]
def change
create_table :cars, id: false, force: true do |t|
t.primary_key :auto_id
t.string :id, null: false
end
end
end
我想:
- 使
primary_key
由数据库自动生成 - 在保存模型之前,直接在应用程序中设置
id
值。
但是当我试图将汽车实例保存到数据库时,我有一个从应用程序设置id
的问题。
class Car < ActiveRecord::Base
self.primary_key = "auto_id"
before_create do
binding.pry
end
end
即使在binding.pry
行,当我调用self.id = '1234'
时,它被重新分配给auto_id
字段,而不是id
。
因此id
列始终保持NULL
,导致DB错误Field 'id' doesn't have a default value
。
[2] pry(#<Car>)> self.id = '9'
=> "9"
[3] pry(#<Car>)> self
=> #<Car:0x00007fcdb34ebe60
auto_id: 9,
id: nil>
p。这是Rails 6。
我会通过将其重命名为其他东西来避免使用id
。在rails中,id
是@primary_key设置的值。有一个完整的模块专门用于定义id属性方法:
https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/PrimaryKey.html
这些方法可以被覆盖,但不应该被覆盖:
class Car < ApplicationRecord
self.primary_key = "auto_id"
def id= value
_write_attribute("id", value)
end
def id
_read_attribute("id")
end
end
"id"是硬编码在write_attribute使用primary_key,所以我们不能使用write_attribute来设置id属性本身。
同样,id
方法也在其他地方使用。重写它们是一个坏主意。
>> Car.create(id: "99")
=> #<Car:0x00007f723e801788 auto_id: nil, id: "99">
# ^
# NOTE: Even though car has been saved, `auto_id` is still `nil`
# but only in `car` object. Because we have overridden
# `id`, primary key is read from `id` column and then `id`
# attribute is set in `id=`. `auto_id` is bypassed.
>> Car.last
=> #<Car:0x00007f774d9e8dd0 auto_id: 1, id: "99">
# NOTE: Other methods are broken whenever `id` is used
>> Car.last.to_key
=> ["99"]
>> Car.last.to_gid
=> #<GlobalID:0x00007f774f4def68 @uri=#<URI::GID gid://stackoverflow/Car/99>>
一个更好的方法是不碰id
方法:
class Car < ApplicationRecord
self.primary_key = "auto_id"
def id_attribute= value
_write_attribute("id", value)
end
def id_attribute
_read_attribute("id")
end
end
>> car = Car.create(id_attribute: "99")
=> #<Car:0x00007fb1e44d9458 auto_id: 2, id: "99">
>> car.id_attribute
=> "99"
>> car.id
=> 2
>> car.auto_id
=> 2