Como posso definir valores padrão no ActiveRecord?

417

Como posso definir o valor padrão no ActiveRecord?

Eu vejo uma postagem do Pratik que descreve um pedaço feio e complicado de código: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

Vi os seguintes exemplos pesquisando no Google:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

e

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

Também vi pessoas colocarem isso em sua migração, mas prefiro vê-lo definido no código do modelo.

Existe uma maneira canônica de definir o valor padrão para os campos no modelo ActiveRecord?

ryw
fonte
Parece que você respondeu a pergunta a si mesmo, em duas variantes diferentes :)
Adam Byrtek
19
Observe que o idioma "padrão" do Ruby para 'self.status = ACTIVE, a menos que self.status' seja 'self.status || = ACTIVE' #
Mike Woodhouse
1
A resposta de Jeff Perrin é muito melhor do que a atualmente marcada como aceita. O default_scope é uma solução inaceitável para definir valores padrão, porque tem o EFEITO LATERAL ENORME de também alterar o comportamento das consultas.
Lawrence
veja também stackoverflow.com/questions/3975161/…
Viktor Trón
2
dado todos os upvotes a esta pergunta, eu diria que o Ruby precisa de um método setDefaultValue para ActiveRecord
spartikus

Respostas:

557

Existem vários problemas com cada um dos métodos disponíveis, mas acredito que definir um after_initializeretorno de chamada é o caminho a seguir pelos seguintes motivos:

  1. default_scopeinicializará valores para novos modelos, mas esse será o escopo no qual você encontra o modelo. Se você deseja inicializar alguns números para 0, isso não é que você deseja.
  2. Definir padrões na sua migração também funciona parte do tempo ... Como já foi mencionado, isso não será funcionará quando você apenas chamar Model.new.
  3. Substituir initializepode funcionar, mas não se esqueça de ligar parasuper !
  4. Usar um plugin como o phusion está ficando um pouco ridículo. Isso é ruby, precisamos mesmo de um plugin apenas para inicializar alguns valores padrão?
  5. A substituição after_initialize está obsoleta a partir do Rails 3. Quando eu substituo after_initializeno Rails 3.0.3, recebo o seguinte aviso no console:

AVISO DE DEPRECÇÃO: A base # after_initialize foi descontinuada, use o método Base.after_initialize:. (chamado de / Users / me / myapp / app / models / my_model: 15)

Portanto, eu diria para escrever um after_initializeretorno de chamada, que permite atributos padrão , além de definir padrões em associações como:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Agora você tem apenas um lugar para procurar a inicialização de seus modelos. Estou usando esse método até que alguém crie um melhor.

Ressalvas:

  1. Para campos booleanos, faça:

    self.bool_field = true if self.bool_field.nil?

    Veja o comentário de Paul Russell nesta resposta para obter mais detalhes

  2. Se você estiver selecionando apenas um subconjunto de colunas para um modelo (por exemplo, usando selectuma consulta como Person.select(:firstname, :lastname).all), receberá um MissingAttributeErrorse o seu initmétodo acessar uma coluna que não foi incluída na selectcláusula. Você pode se proteger contra esse caso da seguinte maneira:

    self.number ||= 0.0 if self.has_attribute? :number

    e para uma coluna booleana ...

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    Observe também que a sintaxe é diferente antes do Rails 3.2 (veja o comentário de Cliff Darling abaixo)

Jeff Perrin
fonte
7
Definitivamente, parece ser a melhor maneira de conseguir isso. O que é realmente estranho e infeliz. Um método preferencial sensato para estabelecer padrões de atributos de modelo na criação parece algo que o Rails já deveria ter construído. A única outra maneira (confiável), sobrescrita initialize, parece realmente complicada para algo que deve ser claro e bem definido. Passei horas pesquisando a documentação antes de pesquisar aqui, porque presumi que essa funcionalidade já estava lá em algum lugar e simplesmente não estava ciente disso.
23411 seaneshbaugh
106
Uma observação sobre isso - se você tiver um campo booleano que deseja usar como padrão, não o faça self.bool_field ||= true, pois isso forçará o campo a true, mesmo se você inicializar explicitamente para false. Em vez disso, faça self.bool_field = true if self.bool_field.nil?.
Paul Russell
2
Em relação ao ponto 2, Model.new realmente funciona (apenas para mim?) Junto com os padrões definidos nas migrações, ou mais exatamente com os valores padrão para as colunas da tabela. Mas reconheço que o método de Jeff baseado no retorno de chamada after_initialize é provavelmente a melhor maneira de fazer. Apenas uma pergunta: funciona com objetos sujos, mas não salvos? No seu exemplo, Person.new.number_was retornará 0.0?
precisa saber é o seguinte
21
Cuidado ao usar essa abordagem combinada à seleção de colunas específicas com registro ativo. Nesse caso, apenas os atributos especificados na consulta serão encontrados no objeto e o código init lançará a MissingAttributeError. Você pode adicionar uma verificação extra, como mostrado: self.number ||= 0.0 if self.has_attribute? :number Para booleans: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Este é o Rails 3.2+ - para uso anterior, self.attributes.has_key?e você precisa de uma string em vez de um símbolo.
Cliff Darling
6
Fazer isso com associações rapidamente carregará essas associações na pesquisa. Comece initializecom return if !new_record?para evitar problemas de desempenho.
precisa
68

Rails 5+

Você pode usar o método de atributo em seus modelos, por exemplo:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

Você também pode passar uma lambda para o defaultparâmetro Exemplo:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
Lucas Caton
fonte
3
ahhhhh esta é a jóia que eu estava procurando! o padrão também pode levar um processo, por exemplo, padrão: -> {Time.current.to_date}
schpet
1
Certifique-se de especificar o tipo como o segundo argumento, caso contrário, o tipo será Valuee nenhuma conversão de tipo será feita.
null
para minha alegria, isso também funciona com store_accessor, por exemplo, dado que store_accessor :my_jsonb_column, :localevocê pode definir #attribute :locale, :string, default: 'en'
Ryan Romanchuk #
Oh, isso é fantástico, eu precisava que os padrões fossem exibidos de uma forma e isso funciona muito bem. Obrigado Lucas.
Paul Watson
Ainda é possível configurá-los para nil. Se eles não podem ser nilDB not null+ DB padrão + github.com/sshaw/keep_defaults são o caminho a percorrer da minha experiência
sshaw
47

Colocamos os valores padrão no banco de dados através de migrações (especificando o :default opção em cada definição de coluna) e permitimos que o Active Record use esses valores para definir o padrão para cada atributo.

IMHO, essa abordagem está alinhada com os princípios da AR: convenção sobre configuração, DRY, a definição da tabela impulsiona o modelo, e não o contrário.

Observe que os padrões ainda estão no código do aplicativo (Ruby), embora não no modelo, mas nas migrações.

Laurent Farcy
fonte
3
Outro problema é quando você deseja um valor padrão para uma chave estrangeira. Você não pode codificar um valor de ID no campo de chave estrangeira porque em bancos de dados diferentes o ID pode ser diferente.
#
2
outro problema é que, dessa maneira, você não pode inicializar acessadores não persistentes (atributos que não são colunas db).
Viktor Trón
2
outro problema é que você não pode necessariamente ver todos os valores padrão em um só lugar. eles podem estar espalhados por diferentes migrações.
Decan
8
Declan, há db / schema.rb
Benjamin Atkin
6
Gostaria de mencionar para futuros leitores: pelo menos pelo que li, isso é contrário aos princípios da RA. A lógica dos modelos deve estar dentro das classes do modelo e os bancos de dados devem ser o mais ignorantes possível. Os valores padrão para mim constituem lógica específica sobre um modelo.
darethas
40

Alguns casos simples podem ser tratados com a definição de um padrão no esquema do banco de dados, mas isso não lida com vários casos mais complicados, incluindo valores calculados e chaves de outros modelos. Para esses casos, faço o seguinte:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

Decidi usar o after_initialize, mas não quero que ele seja aplicado a objetos que são encontrados apenas aqueles novos ou criados. Eu acho que é quase chocante que um retorno de chamada after_new não seja fornecido para este caso de uso óbvio, mas fiz isso confirmando se o objeto já está persistindo, indicando que não é novo.

Tendo visto a resposta de Brad Murray, isso é ainda mais limpo se a condição for movida para a solicitação de retorno de chamada:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end
Joseph Lord
fonte
4
Este é um ponto realmente importante. Eu tenho que imaginar que, na maioria dos casos, definir o padrão em um registro seja feito apenas antes de persistir um novo registro, não ao carregar um registro persistente.
Russell Silva
Cara, você salvou meu dia.
stephanfriedrich
2
Que tal :before_create?
Franklin Yu
Como: before_create lida com chamadas novas e salvas separadas? Eu gostaria de verificar isso e realmente entender antes de mudar para ele.
Joseph Lord
17

O padrão de retorno de chamada after_initialize pode ser aprimorado simplesmente fazendo o seguinte

after_initialize :some_method_goes_here, :if => :new_record?

Isso tem um benefício não trivial se o seu código de inicialização precisar lidar com associações, pois o código a seguir aciona um n + 1 sutil se você ler o registro inicial sem incluir o associado.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end
Brad Murray
fonte
16

Os caras do Phusion têm um plugin interessante para isso.

Milan Novota
fonte
Observe que este plug-in permite que os :defaultvalores nas migrações de esquema 'apenas trabalhem' Model.new.
jchook
Posso obter os :defaultvalores nas migrações para 'apenas trabalhar' Model.new, ao contrário do que Jeff disse em seu post. Trabalho verificado no Rails 4.1.16.
Magne
8

Uma maneira potencial ainda melhor / mais limpa do que as respostas propostas é substituir o acessador, assim:

def status
  self['status'] || ACTIVE
end

Consulte "Substituindo acessadores padrão" na documentação do ActiveRecord :: Base e muito mais no StackOverflow ao usar self .

Peterhurford
fonte
O status ainda será nulo no hash retornado de attributes. Testado em trilhos 5.2.0.
spyle
8

Eu uso a attribute-defaultsgema

Na documentação: execute sudo gem install attribute-defaultse adicione require 'attribute_defaults'ao seu aplicativo.

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"
aidan
fonte
7

Perguntas semelhantes, mas todas possuem um contexto um pouco diferente: - Como crio um valor padrão para atributos no modelo de registro ativo do Rails?

Melhor resposta: depende do que você quer!

Se você deseja que cada objeto comece com um valor: useafter_initialize :init

Deseja que o new.htmlformulário tenha um valor padrão ao abrir a página? use https://stackoverflow.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

Se você deseja que cada objeto tenha um valor calculado a partir da entrada do usuário: usebefore_save :default_values Deseja que o usuário insiraXe, em seguidaY = X+'foo'? usar:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end
Blair Anderson
fonte
4

É para isso que os construtores servem! Substitua o initializemétodo do modelo .

Use o after_initializemétodo

John Topley
fonte
2
Normalmente você está correto, mas nunca deve substituir a inicialização em um modelo ActiveRecord, pois nem sempre isso pode ser chamado. Você deve usar o after_initializemétodo.
9119 Luke Redpath
Usar um default_scope apenas para definir um padrão é CERTAMENTE errado. after_initialize é a resposta correta.
joaomilho
4

Sup galera, acabei fazendo o seguinte:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Funciona como um encanto!

Tony
fonte
3

Isso foi respondido por um longo tempo, mas preciso dos valores padrão com frequência e prefiro não colocá-los no banco de dados. Eu crio uma DefaultValuespreocupação:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

E depois usá-lo nos meus modelos da seguinte forma:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end
clem
fonte
3

Também vi pessoas colocarem isso em sua migração, mas prefiro vê-lo definido no código do modelo.

Existe uma maneira canônica de definir o valor padrão para os campos no modelo ActiveRecord?

O caminho canônico do Rails, antes do Rails 5, era realmente configurá-lo na migração e apenas olhar no db/schema.rb sempre que desejar ver quais valores padrão estão sendo definidos pelo DB para qualquer modelo.

Ao contrário do que afirma a resposta de @Jeff Perrin (que é um pouco antiga), a abordagem de migração aplicará o padrão ao usar Model.new , devido a alguma mágica do Rails. Trabalho verificado no Rails 4.1.16.

A coisa mais simples é geralmente a melhor. Menos dívida de conhecimento e possíveis pontos de confusão na base de código. E 'simplesmente funciona'.

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

Ou, para alterar a coluna sem criar uma nova, faça o seguinte:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

Ou talvez até melhor:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

Consulte o guia oficial do RoR para obter opções nos métodos de alteração de coluna.

o null: false desaprova valores NULL no banco de dados e, como um benefício adicionado, também é atualizado para que todos os registros de banco de dados preexistentes anteriormente nulos sejam definidos com o valor padrão também para este campo. Você pode excluir esse parâmetro na migração, se desejar, mas achei muito útil!

A maneira canônica no Rails 5+ é, como @Lucas Caton disse:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end
Magne
fonte
1

O problema com as soluções after_initialize é que você precisa adicionar um after_initialize a todos os objetos que você procura do banco de dados, independentemente de acessar ou não esse atributo. Sugiro uma abordagem preguiçosa.

Os métodos de atributo (getters) são obviamente métodos próprios, portanto você pode substituí-los e fornecer um padrão. Algo como:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

A menos que, como alguém apontou, você precise executar Foo.find_by_status ('ACTIVE'). Nesse caso, acho que você realmente precisará definir o padrão nas restrições de seu banco de dados, se o banco de dados o suportar.

Jeff Gran
fonte
Esta solução e a alternativa proposta não funcionam no meu caso: eu tenho uma hierarquia de classes STI em que apenas uma classe possui esse atributo e a coluna correspondente que será usada nas condições de consulta do banco de dados.
Cmoran92 20/03/19
1

Corri para problemas com after_initializedando ActiveModel::MissingAttributeErrorerros ao fazer descobertas complexas:

por exemplo:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"search" no .wherehash de condições

Então, acabei fazendo isso substituindo a inicialização desta maneira:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

A superchamada é necessária para garantir que o objeto seja inicializado corretamente ActiveRecord::Baseantes de executar meu código de personalização, ou seja: default_values

Sean
fonte
Eu gosto disso. Eu precisava fazer def initialize(*); super; default_values; endno Rails 5.2.0. Além disso, o valor padrão está disponível, mesmo no .attributeshash.
Spell #
1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end
Mike Breen
fonte
2
Mmmhh ... parece engenhoso no começo, mas depois de pensar um pouco, vejo alguns problemas. Primeiro, todos os valores padrão não estão em um único ponto, mas estão espalhados pela classe (imagine procurá-los ou alterá-los). Segundo e pior, você não pode colocar, posteriormente, um valor nulo (ou mesmo falso)!
paradoja
por que você precisaria definir um valor nulo como padrão? você tira isso da caixa com o AR sem fazer nada. Quanto a false ao usar uma coluna booleana, então você está certo, essa não é a melhor abordagem.
30568 Mike Breen
Não posso falar por outros hábitos de codificação. Não tive problemas porque não disperso meus getters / setters em um arquivo de classe. Além disso, qualquer editor de texto moderno deve facilitar a navegação para um método (shift-cmd-t no textmate).
30568 Mike Breen
@paradoja - Retiro isso, agora vejo onde ele se divide com o uso de null também. Não necessariamente usando nulo como padrão, mas se você realmente deseja alterar o valor para nulo em algum momento. Boa captura @paradoja, obrigado.
Mike Breen
Eu uso esse método, pois ele funciona bem com atributos gerados dinamicamente.
Dale Campbell
0

Embora fazer isso para definir valores padrão seja confuso e complicado na maioria dos casos, você também pode usá-lo :default_scope. Confira o comentário do squil aqui .

skalee
fonte
0

O método after_initialize foi descontinuado, use o retorno de chamada.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

no entanto, usar : default em suas migrações ainda é a maneira mais limpa.

Greg
fonte
4
No Rails 3: o after_initializemétodo NÃO está obsoleto . De fato, o retorno de chamada no estilo macro é um exemplo de IS descontinuado . Detalhes: Guides.rubyonrails.org/…
Zabba
0

Descobri que o uso de um método de validação fornece muito controle sobre a configuração de padrões. Você pode até definir padrões (ou falhar na validação) para atualizações. Você ainda define um valor padrão diferente para inserções versus atualizações, se realmente quiser. Observe que o padrão não será definido até #valid? é chamado.

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Em relação à definição de um método after_initialize, pode haver problemas de desempenho porque o after_initialize também é chamado por cada objeto retornado por: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

Kelvin
fonte
a validação não acontece apenas antes de salvar? E se você quisesse mostrar os padrões antes de salvar?
Nurettin
@ urettin Esse é um bom argumento e posso ver por que você desejaria isso às vezes, mas o OP não mencionou isso como um requisito. Você precisa decidir por si mesmo se deseja a sobrecarga de definir os padrões em todas as instâncias, mesmo que elas não sejam salvas. A alternativa é manter um objeto fictício por perto para que a newação seja reutilizada.
19413 Kelvin
0

Se a coluna for do tipo 'status' e seu modelo se presta ao uso de máquinas de estado, considere usar a gema aasm , após a qual você pode simplesmente fazer

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

Ele ainda não inicializa o valor para registros não salvos, mas é um pouco mais limpo do que criar o seu próprio initou qualquer outra coisa, e você colhe os outros benefícios do aasm, como escopos para todos os seus status.

Pedido ruim
fonte
0

Eu sugiro fortemente o uso da gema "default_value_for": https://github.com/FooBarWidget/default_value_for

Existem alguns cenários complicados que praticamente exigem a substituição do método de inicialização, o que essa gema exige.

Exemplos:

Seu padrão de banco de dados é NULL, seu modelo / padrão definido em ruby ​​é "alguma string", mas você realmente deseja definir o valor como nulo por qualquer motivo:MyModel.new(my_attr: nil)

A maioria das soluções aqui falhará em definir o valor como nulo e, em vez disso, o definirá como o padrão.

OK, então, em vez de usar a ||=abordagem, você muda para my_attr_changed?...

Mas agora imagine que o padrão do banco de dados é "alguma string", o padrão definido pelo modelo / ruby ​​é "alguma outra string", mas em um determinado cenário, você deseja definir o valor como "alguma string" (o padrão do banco de dados):MyModel.new(my_attr: 'some_string')

Isto irá resultar em my_attr_changed?ser falsa porque o valor corresponde ao db padrão, que por sua vez irá disparar seu código padrão definido pelo rubi e defina o valor para "alguma outra string" - mais uma vez, não o que você desejar.


Por esses motivos, não acho que isso possa ser realizado adequadamente com apenas um gancho after_initialize.

Mais uma vez, acho que a gema "default_value_for" está adotando a abordagem correta: https://github.com/FooBarWidget/default_value_for

Etipton
fonte
0

Aqui está uma solução que eu usei que fiquei um pouco surpresa ainda não foi adicionada.

Existem duas partes nele. A primeira parte é definir o padrão na migração real e a segunda parte é adicionar uma validação no modelo, garantindo que a presença seja verdadeira.

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

Então você verá aqui que o padrão já está definido. Agora, na validação, você deseja garantir que sempre haja um valor para a sequência, então faça

 validates :new_team_signature, presence: true

O que isso fará é definir o valor padrão para você. (para mim eu tenho "Bem-vindo à equipe"), e depois darei um passo adiante e garantirá que sempre exista um valor presente para esse objeto.

Espero que ajude!

kdweber89
fonte
0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
shilovk
fonte
-2

use default_scope nos trilhos 3

api doc

O ActiveRecord obscurece a diferença entre o padrão definido no banco de dados (esquema) e o padrão feito no aplicativo (modelo). Durante a inicialização, ele analisa o esquema do banco de dados e anota quaisquer valores padrão especificados lá. Mais tarde, ao criar objetos, ele atribui esses valores padrão especificados no esquema sem tocar no banco de dados.

discussão

Viktor Trón
fonte
se você usar meta_where, o default_scope poderá não funcionar para atribuir padrões a novos objetos de AR devido a um erro.
Viktor Trón
esse problema do meta_where foi corrigido [ metautonomous.lighthouseapp.com/projects/53011/tickets/…
Viktor Trón 2/11/11
3
NÃO use default_scope. Isso fará com que todas as suas consultas adicionem essa condição ao campo que você definiu. É quase NUNCA o que você quer.
brad
@ Brad, engraçado você menciona, eu concordo totalmente, é mau :). veja meu comentário em stackoverflow.com/questions/10680845/… .
Viktor Trón
-3

Nos documentos da API http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Use o before_validationmétodo em seu modelo, ele oferece as opções de criação de inicialização específica para chamadas de criação e atualização, por exemplo, neste exemplo (novamente código usado do exemplo da API api), o campo numérico é inicializado para um cartão de crédito. Você pode facilmente adaptar isso para definir os valores desejados

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Surpreso que o dele não tenha sido sugerido aqui

jamesc
fonte
before_validation não definirá os padrões até que o objeto esteja pronto para persistir. Se o processo precisar ler os padrões antes de persistir, os valores não estarão prontos.
mmell
Você nunca define valores padrão durante a verificação de validações. Nem sequer é um hack. Fazê-lo durante a inicialização
Sachin