Как мне инициализировать атрибуты, когда я создаю объекты в Rails? - PullRequest
2 голосов
/ 17 июня 2010

Clients есть много Invoices. Счета имеют атрибут number, который я хочу инициализировать, увеличивая предыдущий номер счета клиента.

Например:

@client = Client.find(1)
@client.last_invoice_number
> 14
@invoice = @client.invoices.build
@invoice.number
> 15

Я хочу добавить эту функциональность в мою Invoice модель, но я не уверен, как это сделать. Вот как я представляю себе код:

class Invoice < ActiveRecord::Base
  ...
  def initialize(attributes = {})
    client = Client.find(attributes[:client_id])
    attributes[:number] = client.last_invoice_number + 1
    client.update_attributes(:last_invoice_number => client.last_invoice_number + 1)
  end
end

Однако attributes[:client_id] не установлен , когда я звоню @client.invoices.build.

Как и когда инициализируется client_id в счете, и когда я могу использовать его для инициализации number в счете? Могу ли я перенести эту логику в модель или мне придется поместить ее в контроллер?

Ответы [ 4 ]

7 голосов
/ 17 июня 2010

Создание миграции, которая добавляет столбец invoices_number в таблицу пользователей. Тогда в Invoice модели напишите это:

class Invoice < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
  ...
end

Это автоматически увеличит атрибут invoices_count для пользователя после создания счета.

5 голосов
/ 17 июня 2010

как насчет этого:

class Invoice < ActiveRecord::Base
  ...
  def initialize(attributes = {})
    super
    self.number = self.client.invoices.size + 1 unless self.client.nil?
  end
end
1 голос
/ 17 августа 2012

Вот несколько полезных рассуждений о after_initialize за комментарий Джонатана Р. Уоллеса выше: http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html

1 голос
/ 17 июня 2010

Прежде всего, вам не нужно использовать коллекцию атрибутов, вы можете просто сделать self.client_id. Еще лучше, если у вас есть belongs_to :client в вашем Invoice, вы можете просто сделать self.client.last_invoice_number. Наконец, вы почти всегда хотите вызвать исключение в случае сбоя обновления или создания, поэтому привыкните к использованию update_attributes!, что является лучшим выбором по умолчанию. (если у вас есть какие-либо вопросы по поводу этих вопросов, задайте их, и я подробно расскажу)

Теперь, когда это не так, вы столкнулись с проблемой с ActiveRecord, методы инициализатора почти никогда не являются правильным выбором. AR дает вам кучу методов, чтобы подключиться к любой точке жизненного цикла, в которой вы нуждаетесь. Это

after_create
after_destroy
after_save
after_update
after_validation
after_validation_on_create
after_validation_on_update
before_create
before_destroy
before_save
before_update
before_validation
before_validation_on_create
before_validation_on_update

Что вы, вероятно, хотите, это подключиться к before_create. Как то так

def before_create
  self.number ||= self.client.last_invoice_number + 1 unless self.client
end

Что он будет делать, так это то, что он подключит базу данных для вашего клиента, получит последний номер счета, увеличит его на единицу и установит в качестве его нового номера, но только если вы еще не установили номер (| | = назначит, но только если левая сторона равна нулю), и только если вы установили client (или client_id) перед сохранением.

...