Как добавить последовательности в миграцию и использовать их в модели?

Я хочу есть "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.