Как добавить последовательности в миграцию и использовать их в модели? - PullRequest
18 голосов
/ 30 сентября 2011

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

Знаете ли вы хороший способ интегрировать последовательности? Или есть хороший плагин? Приветствую всех ответов!

Ответы [ 4 ]

11 голосов
/ 30 сентября 2011

У меня нет предложений по более «рельсовому способу» обработки пользовательских последовательностей, но я могу рассказать вам, почему поле customer_no не заполняется после сохранения.

Когда ActiveRecord сохраняет новую записьоператор SQL будет возвращать только идентификатор новой записи, а не все ее поля, вы можете увидеть, где это происходит в текущем источнике рельсов, здесь 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
3 голосов
/ 19 октября 2011

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

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

2 голосов
/ 03 августа 2012

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

https://github.com/code42/pg_sequencer

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

2 голосов
/ 24 мая 2012

Я столкнулся с подобной проблемой, но я также добавил :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

Rails 3.2 / Postgres 9.1

...