根据2列定义唯一主键

时间:2012-10-05 12:29:08

标签: ruby-on-rails primary-key composite-primary-key

我想基于2列定义记录的唯一键:'id'和'language'

让用户提交以下字符串:  id = 1 language = en value = blabla english  id = 1 language = fr value = blabla french

我尝试使用set_primary_key和add_index,但它不起作用(add_index:words,[“id”,“language_id”],:unique => true)

我有以下型号:

class Word < ActiveRecord::Base
  belongs_to :dictionnary
  belongs_to :language

  attr_accessible :lang, :rev, :value, :dictionnary_id, :language_id

  validates :value, :uniqueness => true

end  

和这个

class Language < ActiveRecord::Base
    has_many :words

    attr_accessible :lang
end

7 个答案:

答案 0 :(得分:47)

add_index :words, ["id", "language_id"], :unique => true

它应该工作。也许您的数据库中已经存在一些非唯一数据,并且无法创建索引?但是(因为@Doon注意到它将是多余的,因为id总是唯一的)。因此,您需要在两列上创建主键。

要在rails中定义2列主键,请使用:

create_table :words, {:id => false} do |t|
  t.integer :id
  t.integer :language_id
  t.string :value
  t.timestamps
end
execute "ALTER TABLE words ADD PRIMARY KEY (id,language_id);"

使用此gem设置模型中的primary_key:http://rubygems.org/gems/composite_primary_keys

class Word < ActiveRecord::Base
    self.primary_keys = :id,:language_id
end

答案 1 :(得分:6)

在Rails 5中,您可以执行以下操作:

create_table :words, primary_key: %i[id language_id] do |t|
  t.integer :id
  t.integer :language_id
  t.string :value
  t.timestamps
end

也无需在primary_key模型上设置Word属性。

答案 2 :(得分:1)

正如我在评论中所说的那样,如果你尝试这样做,你将会对抗轨道,并且开箱即用并不支持。您可以查看http://compositekeys.rubyforge.org,它提供了在rails中执行复合主键的方法。我没有使用它,因为我还没有需要(通常当我有一些复合键时,就像它只是一个连接表,没有主键和连接对上的唯一索引(HABTM)。 / p>

答案 3 :(得分:1)

<强>模型

class User < ActiveRecord::Base
  has_secure_password
  self.primary_keys = :name
end

<强>移植

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :emailid
      t.string :password_digest
      t.integer :locked, :default => 0
      t.text :secretquestion
      t.string :answer

      t.timestamps null: false
    end
    add_index :users, :name, :unique => true
  end
end

你会得到这张表

image

答案 4 :(得分:0)

根据您的使用情况,您可能需要尝试composite key gem,它允许您定义复合主键,并且还可以增强ActiveRecord来处理这种模型(对于此模型或url_for的关联有很大帮助)帮手等。)。

因此,如果您计划将此模型用作任何其他轨道模型,那么gem将会有很多帮助。

答案 5 :(得分:0)

将网站迁移到Rails时遇到了类似的问题。我有一个表存储我的网站可用的每种语言的文本数据,所以我有这样的东西:

CREATE TABLE Project_Lang(
    project_id INT NOT NULL,
    language_id INT NOT NULL,
    title VARCHAR(80),
    description TEXT,

    PRIMARY KEY pk_Project_Lang(project_id, language_id),

    FOREIGN KEY fk_Project_Lang_Project(project_id)
        REFERENCES Project(project_id)
        ON DELETE RESTRICT ON UPDATE CASCADE,

    FOREIGN KEY fk_Project_Lang_Language(language_id)
        REFERENCES Language(language_id)
        ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_spanish_ci;

但是由于Rails没有开箱即可处理复合主键,因此我不得不改变表的结构,使其拥有自己的主键:

CREATE TABLE Project_Lang(
    project_lang_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    project_id INT NOT NULL,
    language_id INT NOT NULL,
    title VARCHAR(80),
    description TEXT,

    UNIQUE INDEX(project_id, language_id),

    FOREIGN KEY fk_Project_Lang_Project(project_id)
        REFERENCES Project(project_id)
        ON DELETE RESTRICT ON UPDATE CASCADE,

    FOREIGN KEY fk_Project_Lang_Language(language_id)
        REFERENCES Language(language_id)
        ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_spanish_ci;

我还为先前创建复合主键的列创建了一个唯一索引,以便不插入重复记录。然后在我的Rails模型中,我可以简单地说:

self.primary_key = "project_lang_id"

这就是诀窍。不是我想要的,但比打击框架更好。

答案 6 :(得分:0)

就像@ rogal111说的那样,但是如果主键已经存在那么你就会想要这样做

ALTER TABLE sections DROP PRIMARY KEY, ADD PRIMARY KEY(id, workspace_id, section_key);