In einer Join-Tabelle, was die beste Abhilfe für Rails' Abwesenheit eines zusammengesetzten Schlüssels?

StackOverflow https://stackoverflow.com/questions/880981

Frage

create_table :categories_posts, :id => false do |t|
  t.column :category_id, :integer, :null => false
  t.column :post_id, :integer, :null => false
end

Ich habe eine Tabelle verbinden (wie oben) mit Spalten, die zu einem entsprechenden Kategorien Tisch und ein Beiträgen Tabelle. Ich wollte eine eindeutige Einschränkung auf , um den zusammengesetzten Schlüssel category_id, post_id in der categories_posts Join-Tabelle erzwingen. Aber Rails nicht unterstützt dies (ich glaube).

Um das Potenzial für doppelte Zeilen in meinen Daten zu vermeiden, die die gleiche Kombination von category_id und post_id, Was ist die beste Abhilfe für das Fehlen eines zusammengesetzten Schlüssels in Rails ?

Meine Annahmen sind hier:

  1. Die Standard-Auto-Nummernspalte (Id: integer) würde nichts tun, um Schutz meiner Daten in dieser Situation.
  2. ActiveScaffold kann ein liefern Lösung, aber ich bin nicht sicher, ob es ist übertrieben, es schließen in meinem Projekt einfach für dieses Einzel Merkmal, vor allem, wenn es eine elegantere Antwort.
War es hilfreich?

Lösung

Fügen Sie einen eindeutigen Index, der beide Spalten enthält. Das wird verhindern, dass Sie das Einfügen einen Datensatz, der ein doppeltes category_id / post_id Paar enthält.

add_index :categories_posts, [ :category_id, :post_id ], :unique => true, :name => 'by_category_and_post'

Andere Tipps

Es ist sehr hart , um den "richtigen" Ansatz empfehlen.

1) Der pragmatische Ansatz

Verwenden Validator und fügen Sie keine einzigartigen Composite-Index. Dies gibt Ihnen schöner Nachrichten in der Benutzeroberfläche und es funktioniert einfach.

class CategoryPost < ActiveRecord::Base
  belongs_to :category
  belongs_to :post

  validates_uniqueness_of :category_id, :scope => :post_id, :message => "can only have one post assigned"
end

Sie können auch hinzufügen, zwei separate Indizes in Ihrer beitreten Tabellen Suche zu beschleunigen:

add_index :categories_posts, :category_id
add_index :categories_posts, :post_id

Bitte beachten Sie (nach dem Buch Rails 3 Way ) die Validierung wegen einer möglichen Racebedingung zwischen den SELECT und INSERT / UPDATE-Abfragen nicht narrensicher ist . Es empfiehlt sich, eindeutige Einschränkung zu verwenden, wenn Sie absolut sicher sein, müssen es keine doppelten Datensätze.

2) Der kugelsicher Ansatz

In diesem Ansatz wollen wir eine Einschränkung auf der Datenbank-Ebene setzen. So bedeutet es einen zusammengesetzten Index zu erstellen:

add_index :categories_posts, [ :category_id, :post_id ], :unique => true, :name => 'by_category_and_post'

Ein großer Vorteil ist eine große Datenbank-Integrität, Nachteil Berichterstattung an den Benutzer nicht viel nützlicher Fehler ist. Bitte beachten Sie bei der Erstellung von Composite-Index, die Reihenfolge der Spalten ist wichtig.

Wenn Sie setzen weniger selektiv Spalten als führende Spalten in Index und setzten selektivsten Spalten am Ende, andere Abfragen, die Bedingung für Nicht-Leitindex Spalten haben auch die Vorteile des INDEX SKIP SCAN nehmen. Sie müssen möglicherweise einen weiteren Index hinzuzufügen Vorteil von ihnen zu bekommen, aber das ist sehr Datenbank abhängig.

3) Kombination beider

kann man über Kombination von beiden lesen, aber ich neige dazu, die Nummer eins nur zu mögen.

Ich glaube, Sie können leichter finden Einzigartigkeit eines der Felder mit dem anderen als Rahmen zu überprüfen:

DER API:

validates_uniqueness_of(*attr_names)

Dieser Test prüft, ob der Wert der angegebenen Attribute sind einzigartig im gesamten System. Nützlich für die dafür sorgen, dass nur ein Benutzer „davidhh“ genannt werden kann.

  class Person < ActiveRecord::Base
    validates_uniqueness_of :user_name, :scope => :account_id
  end

Es kann bestätigen auch, ob der Wert der angegebenen Attribute sind einzigartig auf der Basis mehrerer Umfang Parameter. Zum Beispiel, um sicherzustellen, dass ein Lehrer nur auf dem Zeitplan einmal pro Semester für eine bestimmte Klasse sein kann.

  class TeacherSchedule < ActiveRecord::Base
    validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
  end

Wenn der Datensatz erstellt wird, wird eine Prüfung durchgeführt wird, um sicherzustellen, dass kein Datensatz mit dem angegebenen Wert für das angegebene Attribut in der Datenbank vorhanden ist (dh Karten auf eine Spalte). Wenn der Datensatz aktualisiert wird, wird die gleiche Prüfung vorgenommen, aber ohne Berücksichtigung der Datensatz selbst.

Konfigurationsoptionen:

* message - Specifies a custom error message (default is: "has already been taken")
* scope - One or more columns by which to limit the scope of the uniquness constraint.
* case_sensitive - Looks for an exact match. Ignored by non-text columns (true by default).
* allow_nil - If set to true, skips this validation if the attribute is null (default is: false)
* if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.

ich implementieren die beiden folgenden, wenn ich dieses Problem in Schienen haben:

1) Sie sollten einen einzigartigen Composite-Index auf Datenbankebene deklariert haben, um sicherzustellen, dass der dbms nicht ein doppelter Datensatz erstellt erhalten lassen.

2) Um eine glattere Fehler msgs als nur die oben, fügen Sie eine Bestätigung an die Rails Modell:

validates_each :category_id, :on => :create do |record, attr, value|
  c = value; p = record.post_id
  if c && p && # If no values, then that problem 
               # will be caught by another validator
    CategoryPost.find_by_category_id_and_post_id(c, p)
    record.errors.add :base, 'This post already has this category'
  end
end

kann eine Lösung sein, sowohl den Index und Validierung im Modell hinzuzufügen.

So in der Migration haben Sie: add_index: categories_posts, [: category_id,: post_id]: unique => true

Und im Modell: validates_uniqueness_of: category_id,: scope => [: category_id,: post_id] validates_uniqueness_of: post_id,: scope => [: category_id,: post_id]

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top