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