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

Я хочу иметь модель «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

Если вы знаете лучший «рельсовый» подход к добавлению последовательностей, было бы здорово сообщить мне об этом.

Теперь, если я сделаю что-то вроде

cust = Accounts::Customer.new
cust.save

поле customer_no предварительно не заполняется следующим значением последовательности (должно быть 1002).

Вы знаете хороший способ интегрировать последовательности? Или есть хороший плагин? Здоровья всем ответам!


person Hiasinho    schedule 30.09.2011    source источник


Ответы (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
person roboles    schedule 30.09.2011

Я считаю, что ответ робота неверен.

Я попытался реализовать это в своем приложении (точно такое же env: RoR+PostgreSQL), и я обнаружил, что когда save выдается на RoR с объектом, имеющим пустые атрибуты, он пытается выполнить INSERT в базе данных, отметив, что все VALUES должен быть установлен в NULL. Проблема заключается в том, как PostgreSQL обрабатывает NULL: в этом случае новая строка будет создана, но все значения будут пустыми, т. е. DEFAULT будет проигнорирован. Если бы save записывал только атрибуты оператора INSERT, заполненные в RoR, это работало бы нормально.

Другими словами, сосредоточившись только на упомянутых выше атрибутах type и customer_no, PostgreSQL ведет себя следующим образом:

СИТУАЦИЯ 1:

INSERT INTO accounts_customers (type, customer_no) VALUES (NULL, NULL);

(вот как работает save в Rails)

Результат: новая строка с пустым type и пустым customer_no

СИТУАЦИЯ 2:

INSERT INTO accounts_customers (type) VALUES (NULL);

Результат: новая строка с пустыми type и customer_no заполнена NEXTVAL последовательности.

У меня есть тема по этому поводу, почитайте:

Ruby on Rails+PostgreSQL: использование пользовательских последовательностей

person Ricardo Melo    schedule 19.10.2011
comment
Хм, в этом ты прав. Моя вина! Я добавил вашу тему в закладки. Посмотрим, что получится. - person Hiasinho; 24.10.2011
comment
Я считаю, что это была ошибка Rails, и это больше не относится к Rails 4+ (github. com/rails/rails/issues/5529) - person nfm; 08.09.2015

Я столкнулся с похожей проблемой, но я также поставил :null => false в поле, чтобы оно автоматически заполнялось nextval.

Что ж, в моем случае AR все еще пытался вставить 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 / Постгрес 9.1

person Alexey Kharchenko    schedule 24.05.2012

Если вы используете PostgreSQL, ознакомьтесь с написанным мной жемчугом pg_sequencer:

https://github.com/code42/pg_sequencer

Он предоставляет DSL для создания, удаления и изменения последовательностей при миграции ActiveRecord.

person Tony Collen    schedule 03.08.2012
comment
Хорошая жемчужина, но не ответ. - person freemanoid; 17.09.2014