Define a unique primary key based on 2 columns

泄露秘密 提交于 2019-12-03 04:14:12

问题


I would like to define a unique key for records based on 2 columns : 'id' and 'language'

to let the user submits the following strings : id=1 language=en value=blabla english id=1 language=fr value=blabla french

I tried to use set_primary_key and also add_index but it didn't work ( add_index :words, ["id", "language_id"], :unique => true )

I have the following model :

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

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

  validates :value, :uniqueness => true

end  

and this

class Language < ActiveRecord::Base
    has_many :words

    attr_accessible :lang
end

回答1:


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

It should work. Maybe you have already some non-unique data in your db and index can't be created? But (as @Doon noticed it will be redundant since id is always unique). So you need create primary key on two columns.

To define 2 column primary key in rails use:

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);"

And set primary_key in your model with this gem: http://rubygems.org/gems/composite_primary_keys:

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



回答2:


In Rails 5 you can do the following:

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

It is also NOT necessary to set the primary_key attribute on the Word model.




回答3:


As I said in my comments you will be fighting rails if you try this, and it isn't really supported out of the box. you can look at http://compositekeys.rubyforge.org which offers a way to do composite primary keys in rails. I haven't used it, as I haven't had a need yet (normally when I have something that is composite key like it is just a join table with no primary key and a unique index on the joined pair (HABTM).




回答4:


Model

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

Migration

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

You will get this table




回答5:


Depending on your use case you might want to try out composite key gem which allows you to define composite primary keys and also ehances ActiveRecord to deal with this kind of model (helps a lot for associations to this model or for url_for helpers etc.).

So if you plan in using this model as any other rails model the gem will help a lot.




回答6:


I faced a similar problem when migrating a site to Rails. I had a table which stores text data for each language my site is available in so I had something like this:

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;

But since Rails doesn't handle composite primary keys out of the box I was forced to change the structure of the table so it had it's own primary key:

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;

I also created a unique index for the columns that previously made the composite primary key so that no duplicate record is inserted. Then in my Rails model I could simply:

self.primary_key = "project_lang_id"

And that did the trick. Is not what I wanted but is better than fighting the framework.




回答7:


Just like @rogal111 said, but if a primary key already exists then you'll want to do this

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


来源:https://stackoverflow.com/questions/12746280/define-a-unique-primary-key-based-on-2-columns

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!