модуль ActiveRecord::ConnectionAdapters::SchemaStatements
Открытые методы экземпляра
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1326 def add_check_constraint(table_name, expression, if_not_exists: false, **options) return unless supports_check_constraints? options = check_constraint_options(table_name, expression, options) return if if_not_exists && check_constraint_exists?(table_name, **options) at = create_alter_table(table_name) at.add_check_constraint(expression, options) execute schema_creation.accept(at) end
Добавляет новое ограничение проверки в таблицу. expression — это String представление проверяемого логического условия.
add_check_constraint :products, "price > 0", name: "price_check"
создаёт:
ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
Хеш options может содержать следующие ключи:
:name-
Имя ограничения. По умолчанию —
chk_rails_<identifier>. :if_not_exists-
Не выдавать ошибку, если ограничение уже существует, а просто игнорировать этот случай.
:validate-
(Только PostgreSQL) Указывает, нужно ли проверять ограничение. По умолчанию —
true.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 655 def add_column(table_name, column_name, type, **options) add_column_def = build_add_column_definition(table_name, column_name, type, **options) return unless add_column_def execute schema_creation.accept(add_column_def) end
Добавляет новый столбец type с именем column_name в table_name.
См. ActiveRecord::ConnectionAdapters::TableDefinition.column.
Параметр type обычно представляет собой один из встроенных типов миграции: :primary_key, :string, :text, :integer, :bigint, :float, :decimal, :numeric, :datetime, :time, :date, :binary, :blob, :boolean.
Можно использовать тип, которого нет в этом списке, если он поддерживается вашей базой данных (например, «polygon» в MySQL), но это лишает код независимости от базы данных и обычно такого следует избегать.
Доступны следующие параметры (по умолчанию ни один из них не задан):
-
:comment— задаёт комментарий к столбцу. Некоторые адаптеры не учитывают этот параметр. -
:collation— задаёт сортировку для столбца:stringили:text. Если параметр не указан, столбец будет использовать ту же сортировку, что и таблица. -
:default— значение столбца по умолчанию. ИспользуйтеnilдляNULL. -
:limit— задаёт максимальную длину столбца. Для столбца:stringэто количество символов, а для столбцов:text,:binary,:blobи:integer— количество байтов. Некоторые адаптеры не учитывают этот параметр. -
:null— разрешает или запрещает значенияNULLв столбце. -
:precision— задаёт точность для столбцов:decimal,:numeric,:datetimeи:time. -
:scale— задаёт масштаб для столбцов:decimalи:numeric. -
:if_not_exists— указывает, что столбец уже существует и его не нужно добавлять повторно. Это позволяет избежать ошибок из-за дублирования столбцов.
Примечание. Точность — это общее количество значащих цифр, а масштаб — количество цифр, которые можно сохранить после десятичной точки. Например, число 123.45 имеет точность 5 и масштаб 2. Десятичное число с точностью 5 и масштабом 2 может принимать значения от -999.99 до 999.99.
Обратите внимание на различия в поведении столбцов :decimal в разных СУБД:
-
Согласно стандарту SQL, масштаб по умолчанию должен быть равен 0,
:scale<=:precision, а требования к:precisionне указаны. -
MySQL:
:precision[1..65],:scale[0..30]. По умолчанию — (10,0). -
PostgreSQL:
:precision[1..infinity],:scale[0..infinity]. Значение по умолчанию отсутствует. -
SQLite3: ограничений на
:precisionи:scaleнет, но максимальное поддерживаемое значение:precisionравно 16. Значение по умолчанию отсутствует. -
Oracle:
:precision[1..38],:scale[-84..127]. По умолчанию — (38,0). -
SqlServer:
:precision[1..38],:scale[0..38]. По умолчанию — (38,0).
Примеры
add_column(:users, :picture, :binary, limit: 2.megabytes) # ALTER TABLE "users" ADD "picture" blob(2097152) add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false) # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2) # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2) add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20) # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20) # While :scale defaults to zero on most databases, it # probably wouldn't hurt to include it. add_column(:measurements, :huge_integer, :decimal, precision: 30) # ALTER TABLE "measurements" ADD "huge_integer" decimal(30) # Defines a column that stores an array of a type. add_column(:users, :skills, :text, array: true) # ALTER TABLE "users" ADD "skills" text[] # Defines a column with a database-specific type. add_column(:shapes, :triangle, 'polygon') # ALTER TABLE "shapes" ADD "triangle" polygon # Ignores the method call if the column exists add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1205 def add_foreign_key(from_table, to_table, **options) return unless use_foreign_keys? options = foreign_key_options(from_table, to_table, options) return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column, :primary_key)) at = create_alter_table from_table at.add_foreign_key to_table, options execute schema_creation.accept(at) end
Добавляет новый внешний ключ. from_table — таблица со столбцом ключа, to_table содержит связанный первичный ключ.
Имя внешнего ключа формируется по следующему шаблону: fk_rails_<identifier>. identifier — строка длиной 10 символов, детерминированно генерируемая на основе from_table и column. Пользовательское имя можно указать с помощью параметра :name.
Создание простого внешнего ключа
add_foreign_key :articles, :authors
создаёт:
ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") Создание внешнего ключа с игнорированием вызова метода, если ключ уже существует
add_foreign_key(:articles, :authors, if_not_exists: true)
Создание внешнего ключа для определённого столбца
add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
создаёт:
ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id") Создание составного внешнего ключа
Assuming "carts" table has "(shop_id, user_id)" as a primary key. add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
создаёт:
ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id") Создание каскадного внешнего ключа
add_foreign_key :articles, :authors, on_delete: :cascade
создаёт:
ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE Хеш options может содержать следующие ключи:
:column-
Имя столбца внешнего ключа в
from_table. По умолчанию —to_table.singularize + "_id". Передайте массив, чтобы создать составной внешний ключ. :primary_key-
Имя столбца первичного ключа в
to_table. По умолчанию —id. Передайте массив, чтобы создать составной внешний ключ. :name-
Имя ограничения. По умолчанию —
fk_rails_<identifier>. :on_delete-
Действие, выполняемое
ON DELETE. Допустимые значения::nullify,:cascadeи:restrict :on_update-
Действие, выполняемое
ON UPDATE. Допустимые значения::nullify,:cascadeи:restrict :if_not_exists-
Указывает, что внешний ключ уже существует и его не нужно добавлять повторно. Это позволяет избежать ошибок из-за дублирования столбцов.
:validate-
(Только PostgreSQL) Указывает, нужно ли проверять ограничение. По умолчанию —
true. :deferrable-
(Только PostgreSQL) Указывает, должен ли внешний ключ быть отложенным. Допустимые значения — логические значения,
:deferredили:immediateдля использования поведения по умолчанию. По умолчанию —false.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 947 def add_index(table_name, column_name, **options) create_index = build_create_index_definition(table_name, column_name, **options) execute schema_creation.accept(create_index) end
Добавляет новый индекс в таблицу. column_name может быть одним Symbol или Array из символов.
Имя индекса формируется на основе имени таблицы и имени (имён) столбца, если только в качестве параметра не передан :name.
Создание простого индекса
add_index(:suppliers, :name)
создаёт:
CREATE INDEX index_suppliers_on_name ON suppliers(name)
Создание уже существующего индекса
add_index(:suppliers, :name, if_not_exists: true)
создаёт:
CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
Примечание. Не поддерживается в MySQL.
Создание уникального индекса
add_index(:accounts, [:branch_id, :party_id], unique: true)
создаёт:
CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
Создание именованного индекса
add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
создаёт:
CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
Создание индекса с заданной длиной ключа
add_index(:accounts, :name, name: 'by_name', length: 10)
создаёт:
CREATE INDEX by_name ON accounts(name(10))
Создание индекса с заданной длиной ключей для нескольких ключей
add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
создаёт:
CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
Примечание. Поддерживается только в MySQL
Создание индекса с порядком сортировки (desc или asc, по умолчанию — asc)
add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
создаёт:
CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
Примечание. MySQL поддерживает порядок индексов начиная с версии 8.0.1 (в более ранних версиях этот синтаксис принимался, но игнорировался).
Создание частичного индекса
add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
создаёт:
CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
Примечание. Частичные индексы поддерживаются только в PostgreSQL и SQLite.
Создание индекса с дополнительными столбцами
add_index(:accounts, :branch_id, include: :party_id)
создаёт:
CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
Примечание. Поддерживается только в PostgreSQL.
Создание индекса, в котором NULL считаются одинаковыми
add_index(:people, :last_name, nulls_not_distinct: true)
создаёт:
CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
Примечание. Поддерживается только в PostgreSQL версии 15.0.0 и выше.
Создание индекса с определённым методом
add_index(:developers, :name, using: 'btree')
создаёт:
CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
Примечание. Поддерживается только в PostgreSQL и MySQL
Создание индекса с определённым классом операторов
add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
# CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
# CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
# CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
Примечание. Поддерживается только в PostgreSQL
Создание индекса определённого типа
add_index(:developers, :name, type: :fulltext)
создаёт:
CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
Примечание. Поддерживается только в MySQL.
Создание индекса с определённым алгоритмом
add_index(:developers, :name, algorithm: :concurrently) # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL add_index(:developers, :name, algorithm: :inplace) # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
Примечание. Поддерживается только в PostgreSQL и MySQL.
Добавление индекса в параллельном режиме не поддерживается внутри транзакции.
Подробнее см. в разделе «Транзакционные миграции».
Создание индекса, который не используется запросами
add_index(:developers, :name, enabled: false)
создаёт:
CREATE INDEX index_developers_on_name ON developers (name) INVISIBLE -- MySQL CREATE INDEX index_developers_on_name ON developers (name) IGNORED -- MariaDB
Примечание. Поддерживается только в MySQL версии 8.0.0 и выше и MariaDB версии 10.6.0 и выше.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1095 def add_reference(table_name, ref_name, **options) ReferenceDefinition.new(ref_name, **options).add(table_name, self) end
Добавляет ссылку. По умолчанию столбец ссылки имеет тип bigint; с помощью параметра :type можно указать другой тип. Если указан параметр :polymorphic, дополнительно добавляется столбец _type.
Хеш options может содержать следующие ключи:
:type-
Тип столбца ссылки. По умолчанию —
:bigint. :index-
Добавить соответствующий индекс. По умолчанию — true. Описание использования этого параметра см. в
add_index. :foreign_key-
Добавить соответствующее ограничение внешнего ключа. По умолчанию — false; передайте true, чтобы добавить его. Если имя таблицы соединения невозможно определить по связи, передайте
:to_tableс соответствующим именем таблицы. :polymorphic-
Добавлять ли дополнительный столбец
_type. По умолчанию — false. :null-
Допускает ли столбец значения null. По умолчанию — true.
Создание столбца user_id типа bigint без индекса
add_reference(:products, :user, index: false)
Создание столбца user_id типа string
add_reference(:products, :user, type: :string)
Создание столбцов supplier_id и supplier_type
add_reference(:products, :supplier, polymorphic: true)
Создание столбца supplier_id с уникальным индексом
add_reference(:products, :supplier, index: { unique: true })
Создание столбца supplier_id с именованным индексом
add_reference(:products, :supplier, index: { name: "my_supplier_index" })
Создание столбца supplier_id и соответствующего внешнего ключа
add_reference(:products, :supplier, foreign_key: true)
Создание столбца supplier_id и внешнего ключа на таблицу firms
add_reference(:products, :supplier, foreign_key: { to_table: :firms })
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1492
def add_timestamps(table_name, **options)
fragments = add_timestamps_for_alter(table_name, **options)
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
end Добавляет в table_name столбцы временных меток (created_at и updated_at). Дополнительные параметры (например, :null) передаются в add_column.
add_timestamps(:suppliers, null: true)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1397
def assume_migrated_upto_version(version)
version = version.to_i
sm_table = quote_table_name(pool.schema_migration.table_name)
migration_context = pool.migration_context
migrated = migration_context.get_all_versions
versions = migration_context.migrations.map(&:version)
unless migrated.include?(version)
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
end
inserting = (versions - migrated).select { |v| v < version }
if inserting.any?
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
end
execute insert_versions_sql(inserting)
end
end # File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 337 def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options) table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options)) table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options)) yield table_definition if block_given? table_definition end
Возвращает объект TableDefinition, содержащий сведения о таблице, которая была бы создана при передаче тех же аргументов в create_table. Сведения о передаче table_name и других дополнительных параметров см. в create_table.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 730 def change_column(table_name, column_name, type, **options) raise NotImplementedError, "change_column is not implemented" end
Изменяет определение столбца в соответствии с новыми параметрами. Подробные сведения о доступных параметрах см. в TableDefinition#column.
change_column(:suppliers, :name, :string, limit: 80) change_column(:accounts, :description, :text)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1570
def change_column_comment(table_name, column_name, comment_or_changes)
raise NotImplementedError, "#{self.class} does not support changing column comments"
end Изменяет комментарий к столбцу или удаляет его, если указано nil.
Передача хеша, содержащего :from и :to, сделает это изменение обратимым в миграции:
change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 748 def change_column_default(table_name, column_name, default_or_changes) raise NotImplementedError, "change_column_default is not implemented" end
Задаёт новое значение по умолчанию для столбца:
change_column_default(:suppliers, :qualification, 'new') change_column_default(:accounts, :authorized, 1)
Если задать значение по умолчанию nil, оно будет удалено:
change_column_default(:users, :email, nil)
Передача хеша, содержащего :from и :to, сделает это изменение обратимым в миграции:
change_column_default(:posts, :state, from: nil, to: "draft")
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 777 def change_column_null(table_name, column_name, null, default = nil) raise NotImplementedError, "change_column_null is not implemented" end
Добавляет или удаляет ограничение NOT NULL для столбца. Флаг null указывает, может ли значение быть NULL. Например:
change_column_null(:users, :nickname, false)
означает, что псевдонимы не могут быть NULL (ограничение добавляется), тогда как
change_column_null(:users, :nickname, true)
разрешает им быть NULL (ограничение удаляется).
Метод принимает необязательный четвёртый аргумент для замены существующих значений NULL на какое-либо другое значение. При необходимости используйте этот аргумент при включении ограничения, поскольку в противном случае эти строки не будут ему соответствовать.
Обратите внимание: четвёртый аргумент не задаёт значение столбца по умолчанию.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 529
def change_table(table_name, base = self, **options)
if supports_bulk_alter? && options[:bulk]
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
yield update_table_definition(table_name, recorder)
bulk_change_table(table_name, recorder.commands)
else
yield update_table_definition(table_name, base)
end
end Блок для изменения столбцов в table.
# change_table() yields a Table instance change_table(:suppliers) do |t| t.column :name, :string, limit: 60 # Other column alterations here end
Хеш options может содержать следующие ключи:
:bulk-
Установите значение true, чтобы выполнить массовый запрос изменения, например:
ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
По умолчанию — false.
Поддерживается только адаптерами
MySQLи PostgreSQL; в остальных случаях игнорируется.
Добавление столбца
change_table(:suppliers) do |t| t.column :name, :string, limit: 60 end
Изменение типа столбца
change_table(:suppliers) do |t| t.change :metadata, :json end
Добавление двух целочисленных столбцов
change_table(:suppliers) do |t| t.integer :width, :height, null: false, default: 0 end
Добавление столбцов created_at/updated_at
change_table(:suppliers) do |t| t.timestamps end
Добавление столбца внешнего ключа
change_table(:suppliers) do |t| t.references :company end
Создаёт столбец company_id(bigint).
Добавление полиморфного столбца внешнего ключа
change_table(:suppliers) do |t| t.belongs_to :company, polymorphic: true end
Создаёт столбцы company_type(varchar) и company_id(bigint).
Удаление столбца
change_table(:suppliers) do |t| t.remove :company end
Удаление нескольких столбцов
change_table(:suppliers) do |t| t.remove :company_id t.remove :width, :height end
Удаление индекса
change_table(:suppliers) do |t| t.remove_index :company_id end
Подробные сведения обо всех доступных преобразованиях столбцов см. также в Table.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1560
def change_table_comment(table_name, comment_or_changes)
raise NotImplementedError, "#{self.class} does not support changing table comments"
end Изменяет комментарий к таблице или удаляет его, если указано nil.
Передача хеша, содержащего :from и :to, сделает это изменение обратимым в миграции:
change_table_comment(:posts, from: "old_comment", to: "new_comment")
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1374
def check_constraint_exists?(table_name, **options)
if !options.key?(:name) && !options.key?(:expression)
raise ArgumentError, "At least one of :name or :expression must be supplied"
end
check_constraint_for(table_name, **options).present?
end Проверяет, существует ли в таблице ограничение проверки, соответствующее заданному определению.
check_constraint_exists?(:products, name: "price_check")
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1306 def check_constraints(table_name) raise NotImplementedError end
Возвращает массив ограничений проверки для указанной таблицы. Ограничения проверки представлены объектами CheckConstraintDefinition.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 133
def column_exists?(table_name, column_name, type = nil, **options)
column_name = column_name.to_s
checks = []
checks << lambda { |c| c.name == column_name }
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
column_options_keys.each do |attr|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
end
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
end Проверяет, существует ли столбец в указанной таблице.
# Check a column exists column_exists?(:suppliers, :name) # Check a column exists of a particular type # # This works for standard non-casted types (eg. string) but is unreliable # for types that may get cast to something else (eg. char, bigint). column_exists?(:suppliers, :name, :string) # Check a column exists with a specific definition column_exists?(:suppliers, :name, :string, limit: 100) column_exists?(:suppliers, :name, :string, default: 'default') column_exists?(:suppliers, :name, :string, null: false) column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 108
def columns(table_name)
table_name = table_name.to_s
definitions = column_definitions(table_name)
definitions.map do |field|
new_column_from_field(table_name, field, definitions)
end
end Возвращает массив объектов Column для таблицы, указанной в table_name.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 408
def create_join_table(table_1, table_2, column_options: {}, **options)
join_table_name = find_join_table_name(table_1, table_2, options)
column_options.reverse_merge!(null: false, index: false)
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
create_table(join_table_name, **options.merge!(id: false)) do |td|
td.references t1_ref, **column_options
td.references t2_ref, **column_options
yield td if block_given?
end
end Создаёт новую таблицу соединения, имя которой формируется на основе лексического порядка первых двух аргументов. Аргументами могут быть String или Symbol.
# Creates a table called 'assemblies_parts' with no id.
create_join_table(:assemblies, :parts)
# Creates a table called 'paper_boxes_papers' with no id.
create_join_table('papers', 'paper_boxes')
Повторяющийся префикс объединяется в один. Это удобно для моделей с пространствами имён, таких как Music::Artist и Music::Record:
# Creates a table called 'music_artists_records' with no id.
create_join_table('music_artists', 'music_records')
Сведения о доступных параметрах column_options см. в connection.add_reference. column_options будет применён к обоим столбцам.
Можно передать хеш options, содержащий следующие ключи:
:table_name-
Задаёт имя таблицы вместо имени по умолчанию.
:options-
Любые дополнительные параметры, которые нужно добавить к определению таблицы.
:temporary-
Создать временную таблицу.
:force-
Установите значение true, чтобы удалить таблицу перед её созданием. По умолчанию — false.
Обратите внимание: create_join_table по умолчанию не создаёт индексы; для этого можно использовать форму с блоком:
create_join_table :products, :categories do |t| t.index :product_id t.index :category_id end
Добавление внешних ключей с каскадным удалением
create_join_table(:assemblies, :parts, column_options: { foreign_key: { on_delete: :cascade } })
создаёт:
CREATE TABLE assemblies_parts (
assembly_id bigint NOT NULL,
part_id bigint NOT NULL,
CONSTRAINT fk_rails_0d8a572d89 FOREIGN KEY ("assembly_id") REFERENCES "assemblies" ("id") ON DELETE CASCADE,
CONSTRAINT fk_rails_ec7b48402b FOREIGN KEY ("part_id") REFERENCES "parts" ("id") ON DELETE CASCADE
) Добавление специфичного для адаптера параметра в сгенерированный SQL ()
create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
создаёт:
CREATE TABLE assemblies_parts ( assembly_id bigint NOT NULL, part_id bigint NOT NULL, ) ENGINE=InnoDB DEFAULT CHARSET=utf8
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 297
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
validate_create_table_options!(options)
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
if force && options.key?(:if_not_exists)
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
end
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
if force
drop_table(table_name, force: force, if_exists: true)
else
schema_cache.clear_data_source_cache!(table_name.to_s)
end
result = execute schema_creation.accept(td)
unless supports_indexes_in_create?
td.indexes.each do |column_name, index_options|
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
end
end
if supports_comments? && !supports_comments_in_create?
if table_comment = td.comment.presence
change_table_comment(table_name, table_comment)
end
td.columns.each do |column|
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
end
end
result
end Создаёт новую таблицу с именем table_name. table_name может быть String или Symbol.
С create_table можно работать двумя способами. Можно использовать блочную форму или обычную форму, например:
Блочная форма
# create_table() passes a TableDefinition object to the block. # This form will not only create the table, but also columns for the # table. create_table(:suppliers) do |t| t.column :name, :string, limit: 60 # Other fields here end
Блочная форма с сокращённой записью
# You can also use the column types as method calls, rather than calling the column method. create_table(:suppliers) do |t| t.string :name, limit: 60 # Other fields here end
Обычная форма
# Creates a table called 'suppliers' with no columns.
create_table(:suppliers)
# Add a column to 'suppliers'.
add_column(:suppliers, :name, :string, {limit: 60})
Хеш options может содержать следующие ключи:
:id-
Следует ли автоматически добавлять столбец первичного ключа. По умолчанию — true. Для таблиц соединений ActiveRecord::Base.has_and_belongs_to_many следует установить значение false.
Для указания типа создаваемого столбца первичного ключа можно использовать
Symbol.Для указания параметров создания столбца первичного ключа можно использовать
Hash. Доступные параметры см. в разделе add_column. :primary_key-
Имя первичного ключа, если он добавляется автоматически. По умолчанию —
id. Если:idимеет значение false, этот параметр игнорируется.Если передан массив, будет создан составной первичный ключ.
Обратите внимание, что модели Active Record автоматически определяют свой первичный ключ. Этого можно избежать, явно задав ключ в модели с помощью self.primary_key=.
:options-
Любые дополнительные параметры, которые нужно добавить к определению таблицы.
:temporary-
Создать временную таблицу.
:force-
Установите значение true, чтобы удалить таблицу перед её созданием. Установите значение
:cascade, чтобы также удалить зависимые объекты. По умолчанию — false. :if_not_exists-
Установите значение true, чтобы не вызывать ошибку, если таблица уже существует. По умолчанию — false.
:as-
SQL-код для создания таблицы. Если используется этот параметр, блок, а также параметры
:idи:primary_keyигнорируются.
Добавление в сгенерированный SQL параметра, специфичного для серверной части ()
create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
генерирует:
CREATE TABLE suppliers ( id bigint auto_increment PRIMARY KEY ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
Переименование столбца первичного ключа
create_table(:objects, primary_key: 'guid') do |t| t.column :name, :string, limit: 80 end
генерирует:
CREATE TABLE objects ( guid bigint auto_increment PRIMARY KEY, name varchar(80) )
Изменение типа столбца первичного ключа
create_table(:tags, id: :string) do |t| t.column :label, :string end
генерирует:
CREATE TABLE tags ( id varchar PRIMARY KEY, label varchar )
Создание составного первичного ключа
create_table(:orders, primary_key: [:product_id, :client_id]) do |t| t.belongs_to :product t.belongs_to :client end
генерирует:
CREATE TABLE orders (
product_id bigint NOT NULL,
client_id bigint NOT NULL
);
ALTER TABLE ONLY "orders"
ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id); Не добавлять столбец первичного ключа
create_table(:categories_suppliers, id: false) do |t| t.column :category_id, :bigint t.column :supplier_id, :bigint end
генерирует:
CREATE TABLE categories_suppliers ( category_id bigint, supplier_id bigint )
Создание временной таблицы на основе запроса
create_table(:long_query, temporary: true, as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
генерирует:
CREATE TEMPORARY TABLE long_query AS SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
Сведения о создании столбцов см. также в разделе TableDefinition#column.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 45 def data_source_exists?(name) query_values(data_source_sql(name), "SCHEMA").any? if name.present? rescue NotImplementedError data_sources.include?(name.to_s) end
Проверяет, существует ли в базе данных источник данных name.
data_source_exists?(:ebooks)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 35 def data_sources query_values(data_source_sql, "SCHEMA") rescue NotImplementedError tables | views end
Возвращает имена отношений, которые можно использовать в качестве источников для моделей Active Record. Для большинства адаптеров это означает все tables и views.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1584
def disable_index(table_name, index_name)
raise NotImplementedError, "#{self.class} does not support disabling indexes"
end Запрещает использование индекса в запросах.
disable_index(:users, :email)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 446 def drop_join_table(table_1, table_2, **options) join_table_name = find_join_table_name(table_1, table_2, options) drop_table(join_table_name, **options) end
Удаляет таблицу соединений, указанную переданными аргументами. Подробности см. в разделах create_join_table и drop_table.
Хотя эта команда игнорирует переданный блок, его может быть полезно указать в методе change миграции, чтобы её можно было отменить. В этом случае блок будет использоваться методом create_join_table.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 559
def drop_table(*table_names, **options)
table_names.each do |table_name|
schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
end
end Удаляет одну или несколько таблиц из базы данных.
:force-
Установите значение
:cascade, чтобы также удалить зависимые объекты. По умолчанию — false. :if_exists-
Установите значение
true, чтобы удалить таблицу, только если она существует. По умолчанию — false.
Хотя эта команда игнорирует большинство options и переданный блок, их может быть полезно указать в методе change миграции, чтобы её можно было отменить. В этом случае options и блок будут использоваться методом create_table, если только вы не укажете несколько таблиц — такой вариант не поддерживается.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1577
def enable_index(table_name, index_name)
raise NotImplementedError, "#{self.class} does not support enabling indexes"
end Разрешает использование индекса в запросах.
enable_index(:users, :email)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1270 def foreign_key_exists?(from_table, to_table = nil, **options) foreign_key_for(from_table, to_table: to_table, **options).present? end
Проверяет, существует ли в таблице внешний ключ, соответствующий заданному определению внешнего ключа.
# Checks to see if a foreign key exists. foreign_key_exists?(:accounts, :branches) # Checks to see if a foreign key on a specified column exists. foreign_key_exists?(:accounts, column: :owner_id) # Checks to see if a foreign key with a custom name exists. foreign_key_exists?(:accounts, name: "special_fk_name")
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1135 def foreign_keys(table_name) raise NotImplementedError, "foreign_keys is not implemented" end
Возвращает массив внешних ключей указанной таблицы. Внешние ключи представлены объектами ForeignKeyDefinition.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 103
def index_exists?(table_name, column_name = nil, **options)
indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
end Проверяет, существует ли в таблице индекс, соответствующий заданному определению индекса.
# Check an index exists index_exists?(:suppliers, :company_id) # Check an index on multiple columns exists index_exists?(:suppliers, [:company_id, :company_type]) # Check a unique index exists index_exists?(:suppliers, :company_id, unique: true) # Check an index with a custom name exists index_exists?(:suppliers, :company_id, name: "idx_company_id") # Check a valid index exists (PostgreSQL only) index_exists?(:suppliers, :company_id, valid: true)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1043
def index_name_exists?(table_name, index_name)
index_name = index_name.to_s
indexes(table_name).detect { |i| i.name == index_name }
end Проверяет, существует ли индекс с указанным именем.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 82 def indexes(table_name) raise NotImplementedError, "#indexes is not implemented" end
Возвращает массив индексов указанной таблицы.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1640 def max_index_name_size 62 end
Возвращает максимальную длину имени индекса в байтах.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 15
def native_database_types
{}
end Возвращает хеш сопоставлений абстрактных типов данных с собственными типами базы данных. Подробности о распознаваемых абстрактных типах данных см. в разделе TableDefinition#column.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1550 def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) end
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 146 def primary_key(table_name) pk = primary_keys(table_name) pk = pk.first unless pk.size > 1 pk end
Возвращает только первичный ключ таблицы
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1357 def remove_check_constraint(table_name, expression = nil, if_exists: false, **options) return unless supports_check_constraints? return if if_exists && !check_constraint_exists?(table_name, **options) chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name at = create_alter_table(table_name) at.drop_check_constraint(chk_name_to_delete) execute schema_creation.accept(at) end
Удаляет из таблицы указанное ограничение проверки. При попытке удалить несуществующее ограничение проверки будет вызвана ошибка.
remove_check_constraint :products, name: "price_check"
Чтобы без ошибки пропустить удаление несуществующего ограничения проверки, используйте параметр if_exists.
remove_check_constraint :products, name: "price_check", if_exists: true
Параметр expression будет проигнорирован, если он указан. Его может быть полезно передать в методе change миграции, чтобы её можно было отменить. В этом случае expression будет использоваться методом add_check_constraint.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 718
def remove_column(table_name, column_name, type = nil, **options)
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
end Удаляет столбец из определения таблицы.
remove_column(:suppliers, :qualification)
Параметры type и options будут проигнорированы, если они указаны. Их может быть полезно передать в методе change миграции, чтобы её можно было отменить. В этом случае type и options будут использоваться методом add_column. В зависимости от используемой базы данных индексы, использующие этот столбец, могут быть автоматически удалены или изменены так, чтобы этот столбец был исключён из индекса.
Если среди переданных параметров есть ключ if_exists, он будет использоваться для проверки отсутствия столбца. Если столбец уже удалён, миграция будет пропущена без ошибки.
remove_column(:suppliers, :qualification, if_exists: true)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 694
def remove_columns(table_name, *column_names, type: nil, **options)
if column_names.empty?
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
end
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
end Удаляет указанные столбцы из определения таблицы.
remove_columns(:suppliers, :qualification, :experience)
Для возможности отменить миграцию можно передать type и другие параметры столбца.
remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1247 def remove_foreign_key(from_table, to_table = nil, **options) return unless use_foreign_keys? return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table, **options.slice(:column)) fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name at = create_alter_table from_table at.drop_foreign_key fk_name_to_delete execute schema_creation.accept(at) end
Удаляет из таблицы указанный внешний ключ. Все переданные параметры будут использоваться для повторного добавления внешнего ключа при откате миграции. Рекомендуется указать все параметры, использованные при создании внешнего ключа, чтобы миграцию можно было корректно отменить.
Удаляет внешний ключ в таблице accounts.branch_id.
remove_foreign_key :accounts, :branches
Удаляет внешний ключ в таблице accounts.owner_id.
remove_foreign_key :accounts, column: :owner_id
Удаляет внешний ключ в таблице accounts.owner_id.
remove_foreign_key :accounts, to_table: :owners
Удаляет внешний ключ с именем special_fk_name в таблице accounts.
remove_foreign_key :accounts, name: :special_fk_name
Перед удалением проверяет наличие внешнего ключа. Несуществующие индексы игнорируются без уведомления.
remove_foreign_key :accounts, :branches, if_exists: true
Хеш options принимает те же ключи, что и SchemaStatements#add_foreign_key, а также
:to_table-
Имя таблицы, содержащей первичный ключ, на который ссылаются.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 998
def remove_index(table_name, column_name = nil, **options)
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
index_name = index_name_for_remove(table_name, column_name, options)
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
end Удаляет из таблицы указанный индекс.
Удаляет индекс для branch_id в таблице accounts, если существует ровно один такой индекс.
remove_index :accounts, :branch_id
Удаляет индекс для branch_id в таблице accounts, если существует ровно один такой индекс.
remove_index :accounts, column: :branch_id
Удаляет индекс для branch_id и party_id в таблице accounts, если существует ровно один такой индекс.
remove_index :accounts, column: [:branch_id, :party_id]
Удаляет индекс с именем by_branch_party в таблице accounts.
remove_index :accounts, name: :by_branch_party
Удаляет индекс для branch_id с именем by_branch_party в таблице accounts.
remove_index :accounts, :branch_id, name: :by_branch_party
Перед удалением проверяет наличие индекса. Несуществующие индексы игнорируются без уведомления.
remove_index :accounts, if_exists: true
Удаляет индекс с именем by_branch_party в таблице accounts concurrently.
remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
Примечание: поддерживается только PostgreSQL.
Параллельное удаление индекса не поддерживается внутри транзакции.
Дополнительные сведения см. в разделе «Транзакционные миграции».
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1114
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
conditional_options = options.slice(:if_exists, :if_not_exists)
if foreign_key
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
if foreign_key.is_a?(Hash)
foreign_key_options = foreign_key.merge(conditional_options)
else
foreign_key_options = { to_table: reference_name, **conditional_options }
end
foreign_key_options[:column] ||= "#{ref_name}_id"
remove_foreign_key(table_name, **foreign_key_options)
end
remove_column(table_name, "#{ref_name}_id", **conditional_options)
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
end Удаляет ссылки. Также удаляет столбец type, если он существует.
Удаление ссылки
remove_reference(:products, :user, index: false)
Удаление полиморфной ссылки
remove_reference(:products, :supplier, polymorphic: true)
Удаление ссылки с внешним ключом
remove_reference(:products, :user, foreign_key: true)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1501 def remove_timestamps(table_name, **options) remove_columns table_name, :updated_at, :created_at end
Удаляет столбцы временных меток (created_at и updated_at) из определения таблицы.
remove_timestamps(:suppliers)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 785 def rename_column(table_name, column_name, new_column_name) raise NotImplementedError, "rename_column is not implemented" end
Переименовывает столбец.
rename_column(:suppliers, :description, :name)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1012
def rename_index(table_name, old_name, new_name)
old_name = old_name.to_s
new_name = new_name.to_s
validate_index_length!(table_name, new_name)
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
return unless old_index_def
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
remove_index(table_name, name: old_name)
end Переименовывает индекс.
Переименовать индекс index_people_on_last_name в index_users_on_last_name:
rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 543 def rename_table(table_name, new_name, **) raise NotImplementedError, "rename_table is not implemented" end
Переименовывает таблицу.
rename_table('octopuses', 'octopi')
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 29
def table_alias_for(table_name)
table_name[0...table_alias_length].tr(".", "_")
end Обрезает псевдоним таблицы в соответствии с ограничениями текущего адаптера.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 24 def table_comment(table_name) nil end
Возвращает комментарий таблицы, хранящийся в метаданных базы данных.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 60 def table_exists?(table_name) query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present? rescue NotImplementedError tables.include?(table_name.to_s) end
Проверяет, существует ли в базе данных таблица table_name.
table_exists?(:developers)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 19 def table_options(table_name) nil end
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 52 def tables query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA") end
Возвращает массив имён таблиц, определённых в базе данных.
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 1592 def use_foreign_keys? supports_foreign_keys? && foreign_keys_enabled? end
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 75 def view_exists?(view_name) query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present? rescue NotImplementedError views.include?(view_name.to_s) end
Проверяет, существует ли в базе данных представление view_name.
view_exists?(:ebooks)
# File activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, line 67 def views query_values(data_source_sql(type: "VIEW"), "SCHEMA") end
Возвращает массив имён представлений, определённых в базе данных.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.