Как добавить последовательности в миграцию и использовать их в модели?
Я хочу есть "Customer
" модель с обычным первичным ключом и другим столбцом для хранения пользовательского "номера клиента". Кроме того, я хочу, чтобы БД обрабатывала номера клиентов по умолчанию. Я думаю, определение последовательности-лучший способ сделать это. Я использую PostgreSQL. Взгляните на мою миграцию:
class CreateAccountsCustomers < ActiveRecord::Migration
def up
say "Creating sequenze for customer number starting at 1002"
execute 'CREATE SEQUENCE customer_no_seq START 1002;'
create_table :accounts_customers do |t|
t.string :type
t.integer :customer_no, :unique => true
t.integer :salutation, :limit => 1
t.string :cp_name_1
t.string :cp_name_2
t.string :cp_name_3
t.string :cp_name_4
t.string :name_first, :limit => 55
t.string :name_last, :limit => 55
t.timestamps
end
say "Adding NEXTVAL('customer_no_seq') to column cust_id"
execute "ALTER TABLE accounts_customers ALTER COLUMN customer_no SET DEFAULT NEXTVAL('customer_no_seq');"
end
def down
drop_table :accounts_customers
execute 'DROP SEQUENCE IF EXISTS customer_no_seq;'
end
end
если вы знаете лучший подход "rails-like" для добавления последовательностей, было бы здорово сообщить мне об этом.
теперь, если я сделаю что-то вроде
cust = Accounts::Customer.new
cust.save
поле customer_no
предварительно не заполняется следующим значением последовательности (должно быть 1002).
знаете ли вы хороший способ интегрировать последовательности? Или есть хороший плагин? Ура всем ответам!
4 ответов
у меня нет предложений по более "рельсовому способу" обработки пользовательских последовательностей, но я могу сказать вам, почему поле customer_no не заполняется после сохранения.
когда ActiveRecord сохраняет новую запись, оператор SQL возвращает только идентификатор новой записи, а не все ее поля, вы можете увидеть, где это происходит в текущем источнике rails здесь https://github.com/rails/rails/blob/cf013a62686b5156336d57d57cb12e9e17b5d462/activerecord/lib/active_record/persistence.rb#L313
чтобы увидеть значение, вам нужно будет перезагрузить объект...
cust = Accounts::Customer.new
cust.save
cust.reload
Если вы всегда хотите это сделать, подумайте о добавлении крючка after_create в свой класс модели...
class Accounts::Customer < ActiveRecord::Base
after_create :reload
end
Я считаю, что roboles ответ не верный.
Я попытался реализовать это в своем приложении (точно такой же env: RoR+PostgreSQL), и я узнал, что когда save
выдается на RoR с объектом, имеющим пустые атрибуты, он пытается выполнить вставку в базе данных, указав, что все значения должны быть установлены в NULL. Проблема в том, как PostgreSQL обрабатывает нули: в этом случае новая строка будет создана, но со всеми значениями пустыми, т. е. по умолчанию будет игнорируемый. Если save
только написал на атрибутах инструкции INSERT, заполненных на RoR, это будет работать нормально.
другими словами, и сосредоточиться только на type
и customer_no
атрибут, упомянутый выше, так ведет себя PostgreSQL:
Ситуация 1:
INSERT INTO accounts_customers (type, customer_no) VALUES (NULL, NULL);
(вот как Rails'save
работает)
результат: новая строка с пустыми type
и пустой customer_no
ситуация 2:
INSERT INTO accounts_customers (type) VALUES (NULL);
результат: новая строка с пустыми type
и customer_no
заполнено NEXTVAL последовательности
у меня есть тема об этом, проверьте это по адресу:
Ruby on Rails+PostgreSQL: использование пользовательских последовательностей
я столкнулся с подобной проблемой, но я также поставил :null => false
на поле прыгает, что он будет автоматически заполнен nextval.
Ну, в моем случае AR все еще пытался вставить NULL
если атрибут не был указан в запросе, и это привело к исключению для нарушения ограничения not-null.
вот мое решение. Я просто удалил этот ключ атрибута из @attributes
и @changed_attributes
и в этом случае postgres правильно поставил ожидаемую последовательность nextval.
Я поместил это в модели:
before_save do
if (@attributes["customer_no"].nil? || @attributes["customer_no"].to_i == 0)
@attributes.delete("customer_no")
@changed_attributes.delete("customer_no")
end
end
Рельсы 3.2 / Postgres 9.1
Если вы используете PostgreSQL, проверьте драгоценный камень, который я написал, pg_sequencer:
https://github.com/code42/pg_sequencer
Он предоставляет DSL для создания, удаления и изменения последовательностей в миграциях ActiveRecord.