Как бороться с условиями xor, рельсами, внешними ключами и базой данных sqlite? - PullRequest
6 голосов
/ 28 декабря 2011

Что я хочу, так это построить его с помощью Rails 3.1: UML

Если A установил идентификатор для b_id, установить идентификатор для * 1006 не должно быть возможным*.И наверняка тоже наоборот.

Хотел бы я сделать на уровне базы данных от миграции (проверить ограничение?).Это как-то возможно?Или это более доступно в модели с проверками?

Моя среда:

  • Ruby 1.9.3
  • Rails 3.1.3
  • SQLite 3.7.3

Ответы [ 2 ]

2 голосов
/ 30 декабря 2011

Вы можете сделать это посредством полиморфных ассоциаций, хотя схема не будет выглядеть точно так же, как у вас, вы можете достичь той же цели, если элемент A принадлежит либо B, либо C, но никогда не обоим.

Вы можете прочитать больше здесь: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

В примере, приведенном по этой ссылке, A это их Picture, а Employee и Proudct это ваши B и C:

(скопировано из источника, указанного выше):

class Picture < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
end

class Employee < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

class Product < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end
0 голосов
/ 31 декабря 2011

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

Rails не имеет поддержки, которую я мог бы найти, поэтому вам нужно будет создать таблицу с raw sql.Вам также нужно изменить дампер схемы на :sql, так как rails не сможет создать schema.rb, который действительно описывает это.

Я написал эту миграцию

class CreateFoos < ActiveRecord::Migration
  def change

    execute <<SQL
      CREATE TABLE foos (
        id INTEGER  PRIMARY KEY,
        x_id INTEGER,
        y_id INTEGER,
        constraint xorit check( (x_id OR y_id) AND NOT(x_id AND y_id))
      )
SQL
  end
end

Затем в консоли rails

Foo.create(:x_id => 1, :y_id => 1) #=> SQLite3::ConstraintException

Как таковой, вы можете создать строку без установленных x_id и y_id.Вы можете изменить это, изменив ограничение,

(x_id IS NOT NULL OR y_id IS NOT NULL ) AND (x_id IS NULL OR y_id IS NULL)

, похоже, работает для меня

...