Rails 3 + postgresql
我希望有一个随机字符串的sha用于列的默认值。
所以,在我的迁移中,我有:
t.string :uniqueid, default: md5(random()::text)
然而我实际上无法生产任何东西,我使用了反引号,引号等。从我看过的例子看来,pg函数似乎只适用于SELECT
语句。
由于
答案 0 :(得分:5)
Rails会尝试解释这个:
t.string :uniqueid, default: md5(random()::text)
Ruby代码和:default => md5(...)
在Ruby中没有任何意义。如果你引用它,那么Rails会认为它是一个字符串,并为uniqueid
字符串'md5(random()::text)'
设置默认值,这对你没有帮助。
你的问题是Rails不理解使用函数作为列默认的复杂事情,Rails有一个奇怪的,误导的概念,你应该在Rails中而不是在数据库中做所有事情。如果要在默认列中使用函数调用,可以手动执行alter table
:
connection.execute(%q{
alter table your_table alter column uniqueid set default md5(random()::text)
})
这将为您提供数据库中您想要的默认值,但您可能会注意到schema.rb
中没有提及新的默认值。如果你想要一个可用的模式,那么你当然必须将Rails推开并使用SQL schema instead by putting this in your application.rb
:
config.active_record.schema_format = :sql
请注意,SQL架构转储在3.2之前一直被破坏,并且各种Rails版本中存在架构加载问题(但您可以随时psql < structure.sql
解决这个问题。然后删除schema.rb
并改为使用structure.sql
。从好的方面来说,SQL架构转储将跟踪真实的外键,检查约束,触发器...... ...
digest
function from pgcrypto
。
当然,这些都不会起作用,因为ActiveRecord太愚蠢了,不能单独留下它不理解的东西。 ActiveRecord在分析从数据库获取的表定义时会看到默认值,但是它不会理解它,所以它会忽略它。然后,在INSERT期间,ActiveRecord似乎坚持为所有列提供值,甚至是您没有为其赋值的列。结果是,即使您在数据库中附加了合理的默认值,您最终也会在uniqueid
中找到NULL。
那么,你怎么解决这个问题呢?像往常一样使用ActiveRecord,你假装数据库是一个愚蠢的电子表格,并使用可能的before_create
钩子在Ruby中做所有事情。或者你可以找到一个比ActiveRecord更少数据库恶意的ORM;我不确定其他ORM如何处理非常量列默认值。
或者,您可以尝试编写BEFORE INSERT触发器,如果NEW.uniqueid IS NULL
将默认值分配给列。
答案 1 :(得分:3)
您也可以
t.string :token, default: -> { "md5((random())::text)" }, null: false
编辑:即使生成32位数字的随机字符串,也不意味着它是唯一的。我以为是这样,但是我只是遇到了一个独特的索引错误。