Как добавить проверочное ограничение в миграцию Rails? - PullRequest
29 голосов
/ 12 января 2011

Мне нужно добавить новый целочисленный столбец в существующую таблицу в моем приложении Rails. Столбец может иметь только значения 1, 2, 3, поэтому я хотел бы добавить проверочное ограничение к таблице / столбцу. Как мне указать это ограничение в миграции Rails?

Ответы [ 5 ]

43 голосов
/ 12 января 2011

Миграция Rails не предоставляет никакого способа добавления ограничений, но вы все равно можете сделать это с помощью миграции, но передавая фактический SQL для execute ()

Создать файл миграции:

ruby script/generate Migration AddConstraint

Теперь в файле миграции:

class AddConstraint < ActiveRecord::Migration
  def self.up
    execute "ALTER TABLE table_name ADD CONSTRAINT check_constraint_name CHECK (check_column_name IN (1, 2, 3) )"
  end

  def self.down
    execute "ALTER TABLE table_name DROP CONSTRAINT check_constraint_name"
  end
end
3 голосов
/ 17 марта 2017

Я только что опубликовал гем для этого: active_record-postgres-constraints . Как там описывается README , вы можете использовать его с файлом db / schema.rb, и он добавляет поддержку следующих методов в миграциях:

create_table TABLE_NAME do |t|
  # Add columns
  t.check_constraint conditions
  # conditions can be a String, Array or Hash
end

add_check_constraint TABLE_NAME, conditions
remove_check_constraint TABLE_NAME, CONSTRAINT_NAME

Обратите внимание, что в настоящее время поддерживается только postgres.

3 голосов
/ 28 января 2015

Вы можете сделать это с гемом Migration Validators. Подробности здесь: https://github.com/vprokopchuk256/mv-core

С этим камнем вы сможете определить валидацию включения на уровне БД:

def change
  change_table :table_name do |t|
    t.integer :column_name, inclusion: [1, 2, 3]
  end
end

кроме того, вы можете определить, как должна быть определена эта проверка, и даже сообщение об ошибке, которое должно быть показано:

def change
  change_table :posts do |t|
    t.integer :priority, 
              inclusion: { in: [1, 2, 3], 
                           as: :trigger, 
                           message: "can't be anything else than 1, 2, or 3" }
  end
end

вы даже можете повысить уровень этой проверки от миграции прямо к вашей модели:

class Post < ActiveRecord::Base 
  enforce_migration_validations
end

, а затем определение в миграции также будет определено как проверка ActiveModel в вашей модели:

Post.new(priority: 3).valid? 
=> true

Post.new(priority: 4).valid?
=> false

Post.new(priority: 4).errors.full_messages
=> ["Priority can't be anything else than 1, 2, or 3"]
3 голосов
/ 19 марта 2012

Я только что работал, чтобы заставить работать ограничение 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? (Вы не можете просто кэшировать результат из выбора, потому что добавление проверки / ограничения происходит в транзакции, которая может откатываться, поэтому вам нужно проверять каждый раз.)

0 голосов
/ 21 марта 2016

Вы можете использовать Sequel камень https://github.com/jeremyevans/sequel

Sequel.migration do
  change do
    create_table(:artists) do
      primary_key :id
      String :name
      constraint(:name_min_length){char_length(name) > 2}
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...