Подтвердить что ты не робот

В таблице соединений, какой лучший способ обхода Rails отсутствует составной ключ?

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

У меня есть таблица соединений (как указано выше) со столбцами, которые относятся к соответствующей таблице категорий и таблице сообщений. Я хотел бы применить уникальное ограничение для составного ключа category_id, post_id в таблице соединений categories_posts. Но Rails этого не поддерживает (я считаю).

Чтобы избежать возможности дублирования строк в моих данных, имеющих одну и ту же комбинацию category_id и post_id, , что является лучшим обходным решением для отсутствия составного ключа в Rails?

Мои предположения здесь:

  • Столбец автосочетания по умолчанию (id: integer) ничего не сделал бы для защитите мои данные в этой ситуации.
  • ActiveScaffold может предоставить но я не уверен, если это излишне включать его в мою проект просто для этого сингла , особенно если есть более элегантный ответ.
4b9b3361

Ответ 1

Добавьте уникальный индекс, содержащий оба столбца. Это не позволит вам вставить запись, содержащую двойную пару category_id/post_id.

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

Ответ 2

Я думаю, вы можете легче проверить уникальность одного из полей с другим как область действия:

ОТ API:

validates_uniqueness_of(*attr_names)

Проверяет, является ли значение указанных атрибутов уникальным в системе. Полезно для обеспечения того, чтобы только один пользователь мог называться "davidhh".

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

Он также может проверить, является ли значение указанных атрибутов уникальным на основе нескольких параметров области. Например, убедитесь, что учитель может быть только по расписанию один раз в семестр для определенного класса.

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

Когда запись создана, выполняется проверка, чтобы убедиться, что в базе данных нет записи с заданным значением для указанного атрибута (который сопоставляется столбцу). Когда запись обновляется, выполняется одна и та же проверка, но без учета самой записи.

Параметры конфигурации:

* 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.

Ответ 3

Это очень сложно, чтобы рекомендовать "правильный" подход.

1) Прагматический подход

Использовать валидатор и не добавлять уникальный составной индекс. Это дает вам приятные сообщения в пользовательском интерфейсе, и это просто работает.

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

Вы также можете добавить два отдельных индекса в свои таблицы соединений, чтобы ускорить поиск:

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

Обратите внимание (согласно книге Rails 3 Way) проверка не является надежной из-за потенциального состояния гонки между запросами SELECT и INSERT/UPDATE. Рекомендуется использовать уникальное ограничение, если вы абсолютно уверены, что нет дубликатов записей.

2) Пуленепробиваемый подход

В этом подходе мы хотим поставить ограничение на уровень базы данных. Таким образом, это означает создание составного индекса:

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

Большое преимущество - отличная целостность базы данных, недостатком является не очень полезная отчетность об ошибках пользователю. Обратите внимание, что при создании составного индекса важно иметь порядок столбцов.

Если вы помещаете меньше выборочных столбцов в качестве ведущих столбцов в индекс и помещаете большинство выборочных столбцов в конец, другие запросы, которые имеют условие для столбцов не ведущих индексов, также могут использовать INDEX SKIP SCAN. Возможно, вам придется добавить еще один индекс, чтобы получить от них выгоду, но это зависит от базы данных.

3) Сочетание как

Можно прочитать о комбинации обоих, но мне нравится только номер один.

Ответ 4

При использовании этой проблемы в рельсах я реализую оба этих параметра:

1) У вас должен быть уникальный составной индекс, объявленный на уровне базы данных, чтобы гарантировать, что dbms не позволит создать дублируемую запись.

2) Чтобы обеспечить более плавные сообщения об ошибках, чем просто выше, добавьте проверку в модель Rails:

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

Ответ 5

Решением может быть добавление индекса и валидации в модели.

Итак, в процессе миграции у вас есть: add_index: categories_posts, [: category_id,: post_id],: unique = > true

И в модели: validates_uniqueness_of: category_id,: scope = > [: category_id,: post_id] validates_uniqueness_of: post_id,: scope = > [: category_id,: post_id]