我的模型有一个非导轨的传统主键。
class Guoid < ActiveRecord::Base
self.primary_key = :guoid
end
和相关的迁移
class CreateGuoids < ActiveRecord::Migration
def change
create_table :guoids, id: false do |t|
t.integer :guoid, limit: 8, auto_increment: true, primary_key: true
t.integer :parent_guoid, limit: 8
t.string :resource_type, limit: 60
end
end
end
现在我想在另一个模型中引用此模型,并尝试使用references
创建无法工作的迁移。
class ContentUnit < ActiveRecord::Base
self.primary_key = :guoid
end
class Content < ActiveRecord::Base
self.primary_key = :guoid
belongs_to :user
belongs_to :content_unit
end
和相关的迁移
class CreateContents < ActiveRecord::Migration
def change
create_table :contents, id: false do |t|
t.references :content_unit, index: true, foreign_key: true
t.references :user, index: true, foreign_key: true
end
end
end
当我运行迁移时,我收到了以下错误。
Mysql2::Error: Can't create table `myapp_development_cr1`.`#sql-54a_308` (errno: 150 "Foreign key constraint is incorrectly formed"): ALTER TABLE `contents` ADD CONSTRAINT `fk_rails_823443bd0d`
FOREIGN KEY (`content_unit_id`)
REFERENCES `content_units` (`id`)
我希望在content_unit_guoid
表中引用contents
表中创建guoid
个外键。
我使用activerecord-mysql-awesome gem来处理非rails约定主键。
这是一个触发器,它首先在guoids
表中创建一个记录,并使用它的pk作为目标表的pk。
guids
答案 0 :(得分:2)
对于ActiveRecord甚至一般来说,这不是一个可行的数据库设计。
ActiveRecord(以及任何体面的ORM)要求每个表都有一个主键。这就是启用关系并让Rails区分记录的原因。
class Content < ActiveRecord::Base
self.primary_key = :guoid
belongs_to :user
end
由于self.primary_key = :guoid
引用contents.guoid
而非guoids.guoid
,因此无效。您不能将ActiveRecord中的关系用作主键。即使你可能这真的是性能杀手,因为每个查询都需要加入guoids
表 - 甚至递归!
Rails是强烈的约定驱动,如果你花一点时间学习Rails的方式来对抗框架,让它像框架X一样工作,那么你真的会微笑。轨道指南是一个很好的起点。
主键为id
,外键列为_id
。如果你必须与其他开发人员合作,你将不那么大惊小怪,不要像白痴一样对待。
在某些有效情况下,您可能希望使用唯一标识符(UUID)而不是自动递增值。例如,如果您有多个数据库,则自动递增值可能会导致竞争条件。但在这种情况下,您仍然需要在每个表上使用主键 - 区别仅在于主键的内容。
这可以通过使用在应用程序级别上具有较低碰撞机会的算法生成哈希,或者最近通过在数据库中使用二进制UUID类型来生成。后者今天更受欢迎。
不使用关系。 AR就是这样不行。
如果您遵循惯例,belongs_to
和reference
宏就是一个可行的示例。
对于与您在迁移中手动创建它所需的约定不匹配的外键约束:
class CreateStores < ActiveRecord::Migration
def change
create_table :contents do |t|
t.references :manger, index: true, foreign_key: false
end
add_foreign_key :stores, :users, column: 'manager_id', primary_key: 'uuid'
end
end
请注意,这不会解决您的问题,因为您的一般方法不可行!
答案 1 :(得分:1)
那么,你试图使用这个正确的内容表到guoids表创建一个外键吗?
t.references :content_unit, index: true, foreign_key: true
引用将表名作为参数,并尝试在其上查找名为id的列,以在表之间生成外键。因此,您可以在错误消息中看到它试图在content_units表上找到列ID。这绝不是指你的guoids。
想要全局唯一标识符(通常是GUID或UUID)是完全合理的,但是我不知道为什么要将它们存储在一个单独的表中,然后(我假设)将创建一些外键来创建一些大量多对多的表连接数据库中的每个表?看起来真的不可扩展.Postgress很好地处理你的uuid,但正如我过去的,它看起来像你使用mysql。这就是我如何做到的。
Class Teacher < ActiveRecord::Base
has_many :students
before_validation :generate_id
self.primary_key = :id
private
def generate_id
write_attribute(:id, SecureRandom.uuid) unless read_attribute(:id)
end
end
Class Student < ActiveRecord::Base
belongs_to :teacher
end
create_table :teachers, id: false do |t|
t.string :id, null: false, unique: true, limit: 36
end
create_table :students do |t|
t.string :teacher_id, limit: 36
end
add_foreign_key :model_a, :model_b