Я только что работал, чтобы заставить работать ограничение PostgreSQL CHECK.
Решение Нилеша не совсем полное; файл db / schema.rb не будет содержать ограничение, поэтому тесты и любые развертывания, использующие db: setup, не получат ограничение. Согласно http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps
Во время миграции вы можете выполнять собственные операторы SQL,
дампер схемы не может восстановить эти операторы из базы данных.
Если вы используете такие функции, как этот, вы должны установить схему
формат до: sql.
Т.е. в наборе config / application.rb
config.active_record.schema_format = :sql
К сожалению, если вы используете PostgreSQL, вы можете получить ошибку при загрузке результирующего дампа, см. Обсуждение в ОШИБКА: должен быть владельцем языка plpgsql . Я не хотел идти по пути настройки PostgreSQL в этом обсуждении; плюс в любом случае мне нравится иметь читаемый файл db / schema.rb. Так что это исключило для меня пользовательский SQL в файле миграции.
Драгоценный камень https://github.com/vprokopchuk256/mv-core, предложенный Валерой, кажется многообещающим, но он поддерживает только ограниченный набор ограничений (и я получил ошибку, когда попытался его использовать, хотя это может быть связано с несовместимостью с другими драгоценными камнями, которые я в том числе).
Решение (хак), с которым я пошел, состоит в том, чтобы код модели вставил ограничение. Так как это похоже на валидацию, вот где я ее написал:
class MyModel < ActiveRecord::Base
validates :my_constraint
def my_constraint
unless MyModel.connection.execute("SELECT * FROM information_schema.check_constraints WHERE constraint_name = 'my_constraint'").any?
MyModel.connection.execute("ALTER TABLE my_models ADD CONSTRAINT my_constraint CHECK ( ...the SQL expression goes here ... )")
end
end
Конечно, это делает дополнительный выбор перед каждой проверкой; если это проблема, решение будет заключаться в том, чтобы поместить его в патч «после подключения», как описано в Как запустить определенный скрипт после подключения к оракулу с помощью rails? (Вы не можете просто кэшировать результат из выбора, потому что добавление проверки / ограничения происходит в транзакции, которая может откатываться, поэтому вам нужно проверять каждый раз.)