web-dev-qa-db-fra.com

Définir une clé primaire unique basée sur 2 colonnes

Je voudrais définir une clé unique pour les enregistrements basée sur 2 colonnes: 'id' et 'langue'

laisser l'utilisateur soumettre les chaînes suivantes: id = 1 langue = en valeur = blabla anglais id = 1 langue = fr valeur = blabla français

J'ai essayé d'utiliser set_primary_key et également add_index mais cela n'a pas fonctionné (add_index: mots, ["id", "id_langue"],: unique => true)

J'ai le modèle suivant:

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

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

  validates :value, :uniqueness => true

end  

et ça

class Language < ActiveRecord::Base
    has_many :words

    attr_accessible :lang
end
32
Stéphane V

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

Ça devrait marcher. Peut-être avez-vous déjà des données non uniques dans votre base de données et votre index ne peut pas être créé? Mais (comme @Doon l’a remarqué, il sera redondant car id est toujours unique). Il faut donc créer une clé primaire sur deux colonnes.

Pour définir une clé primaire à 2 colonnes dans Rails, utilisez:

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

Et définissez primary_key dans votre modèle avec cette gemme: http://rubygems.org/gems/composite_primary_keys :

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

Dans Rails 5, vous pouvez effectuer les opérations suivantes:

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

Il n'est PAS non plus nécessaire de définir l'attribut primary_key sur le modèle Word.

5
Vadim

Comme je l'ai dit dans mes commentaires, si vous essayez cela, vous allez vous battre contre Rails, et ce n'est pas vraiment pris en charge immédiatement. vous pouvez consulter http://compositekeys.rubyforge.org qui offre un moyen de créer des clés primaires composites dans Rails. Je ne l'ai pas utilisé, car je n'ai pas encore eu besoin de quoi que ce soit (normalement, lorsque j'ai une clé composite, comme une table de jointure sans clé primaire et un index unique sur la paire jointe (HABTM).

1
Doon

Modèle

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

Vous obtiendrez cette table

image

1
Bhavnesh

Comme @ rogal111, mais si une clé primaire existe déjà, vous voudrez le faire.

ALTER TABLE sections DROP PRIMARY KEY, ADD PRIMARY KEY(id, workspace_id, section_key);
0
Son Tr.

En fonction de votre cas d'utilisation, vous pouvez essayer composite key gem qui vous permet de définir des clés primaires composites et également d'améliorer ActiveRecord pour traiter ce type de modèle (aide beaucoup pour les associations à ce modèle ou pour url_for helpers etc.).

Donc, si vous envisagez d’utiliser ce modèle comme tout autre modèle Rails, la gemme aidera beaucoup.

0
Michael

J'ai rencontré un problème similaire lors de la migration d'un site vers Rails. J'avais une table qui stockait des données textuelles pour chaque langue sur laquelle mon site était disponible, donc j'avais quelque chose comme ceci:

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;

Mais comme Rails ne gère pas les clés primaires composites prêtes à l'emploi, j'ai été contraint de modifier la structure de la table pour qu'elle possède sa propre clé primaire:

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;

J'ai également créé un index unique pour les colonnes qui constituaient auparavant la clé primaire composite afin qu'aucun enregistrement en double ne soit inséré. Ensuite, dans mon modèle Rails, je pourrais simplement:

self.primary_key = "project_lang_id"

Et ça a fait l'affaire. N'est-ce pas ce que je voulais mais c'est mieux que de lutter contre le cadre.

0
Sergio