Come posso impostare i valori predefiniti in ActiveRecord?


417

Come posso impostare il valore predefinito in ActiveRecord?

Vedo un post di Pratik che descrive un brutto, complicato pezzo di codice: 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

Ho visto i seguenti esempi googling in giro:

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

e

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

Ho anche visto persone inserirlo nella loro migrazione, ma preferirei vederlo definito nel codice modello.

Esiste un modo canonico per impostare il valore predefinito per i campi nel modello ActiveRecord?


Sembra che tu abbia risposto tu stesso alla domanda, in due diverse varianti :)
Adam Byrtek,

19
Si noti che il linguaggio "standard" di Ruby per "self.status = ACTIVE a meno che self.status" sia "self.status || = ACTIVE"
Mike Woodhouse,

1
La risposta di Jeff Perrin è molto migliore di quella attualmente contrassegnata come accettata. default_scope è una soluzione inaccettabile per l'impostazione dei valori predefiniti, poiché ha il GRANDE EFFETTO LATERALE di modificare anche il comportamento delle query.
lawrence


2
dati tutti i voti a questa domanda, direi che Ruby ha bisogno di un metodo setDefaultValue per ActiveRecord
spartikus,

Risposte:


557

Esistono diversi problemi con ciascuno dei metodi disponibili, ma credo che la definizione di un after_initializecallback sia la strada da percorrere per i seguenti motivi:

  1. default_scopeinizializzerà i valori per i nuovi modelli, ma questo diventerà l'ambito su cui trovare il modello. Se vuoi solo inizializzare alcuni numeri su 0, questo non è quello che vuoi.
  2. La definizione delle impostazioni predefinite nella migrazione funziona anche in parte del tempo ... Come già accennato, questo non funzionerà quando si chiama Model.new.
  3. L'override initializepuò funzionare, ma non dimenticare di chiamare super!
  4. L'uso di un plugin come quello di phusion sta diventando un po 'ridicolo. Questo è ruby, abbiamo davvero bisogno di un plugin solo per inizializzare alcuni valori predefiniti?
  5. L'override after_initialize è obsoleto a partire da Rails 3. Quando after_initializeeseguo l' override in Rails 3.0.3 ricevo il seguente avviso nella console:

AVVISO DI DEPRECAZIONE: Base # after_initialize è stato deprecato, utilizzare invece Base.after_initialize: method. (chiamato da / Users / me / myapp / app / models / my_model: 15)

Pertanto, direi di scrivere un after_initializecallback, che ti consente di impostare attributi predefiniti oltre a permetterti di impostare valori predefiniti su associazioni in questo modo:

  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    

Ora ne hai solo uno posto dove cercare l'inizializzazione dei tuoi modelli. Sto usando questo metodo fino a quando qualcuno ne esce uno migliore.

Avvertenze:

  1. Per i campi booleani:

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

    Vedi il commento di Paul Russell su questa risposta per maggiori dettagli

  2. Se stai selezionando solo un sottoinsieme di colonne per un modello (es. Usando selectuna query simile Person.select(:firstname, :lastname).all) otterrai un MissingAttributeErrorse il tuo initmetodo accede a una colonna che non è stata inclusa nella selectclausola. Puoi proteggerti da questo caso in questo modo:

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

    e per una colonna booleana ...

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

    Si noti inoltre che la sintassi è diversa prima di Rails 3.2 (vedere il commento di Cliff Darling di seguito)


7
Questo sembra sicuramente essere il modo migliore per raggiungere questo obiettivo. Il che è davvero strano e sfortunato. Un metodo sensato preferito per stabilire le impostazioni predefinite degli attributi del modello al momento della creazione sembra qualcosa che Rails dovrebbe già avere incorporato. L'unico altro modo (affidabile), prevalente initialize, sembra davvero contorto per qualcosa che dovrebbe essere chiaro e ben definito. Ho passato ore a gattonare la documentazione prima di cercare qui perché pensavo che questa funzionalità fosse già lì da qualche parte e non ne ero a conoscenza.
seaneshbaugh,

106
Una nota al riguardo: se si dispone di un campo booleano che si desidera impostare come predefinito, non farlo self.bool_field ||= true, poiché ciò costringerà il campo a true anche se lo si inizializza esplicitamente su false. Invece farlo self.bool_field = true if self.bool_field.nil?.
Paul Russell,

2
Per quanto riguarda il punto 2, Model.new funziona effettivamente (solo per me?) Insieme ai valori predefiniti definiti nelle migrazioni, o più esattamente con i valori predefiniti per le colonne della tabella. Ma riconosco che il metodo di Jeff basato sul callback after_initialize è probabilmente il modo migliore per farlo. Solo una domanda: funziona con oggetti sporchi ma non salvati? Nel tuo esempio, Person.new.number_was restituirà 0.0?
Laurent Farcy,

21
Attenzione quando si utilizza questo approccio combinato con la selezione di colonne specifiche con record attivo. In questo caso, solo gli attributi specificati nella query verranno trovati nell'oggetto e il codice init genererà un MissingAttributeError. È possibile aggiungere un controllo supplementare, come illustrato: self.number ||= 0.0 if self.has_attribute? :number Per booleani: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Questo è Rails 3.2+ - per un uso precedente, self.attributes.has_key?è necessario utilizzare una stringa anziché un simbolo.
Cliff Darling,

6
Fare questo con le associazioni caricherà con impazienza quelle associazioni sulla ricerca. Inizia initializecon return if !new_record?per evitare problemi di prestazioni.
Kyle Macey,

68

Rotaie 5+

Puoi utilizzare il metodo degli attributi nei tuoi modelli, ad es .:

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

Puoi anche passare un lambda al defaultparametro. Esempio:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }

3
ahhhhh questa è la gemma che stavo cercando! default può richiedere anche un proc, ad esempio default: -> {Time.current.to_date}
schpet

1
Assicurati di specificare il tipo come secondo argomento, altrimenti il ​​tipo sarà Valuee non verrà eseguita la tipografia.
null,

con mia grande gioia, questo funziona anche con store_accessor, ad esempio dato store_accessor :my_jsonb_column, :localeche puoi definireattribute :locale, :string, default: 'en'
Ryan Romanchuk

Oh, è fantastico, avevo bisogno delle impostazioni predefinite per mostrarle in una forma e questo funziona alla grande. Grazie Lucas.
Paul Watson,

Si possono ancora impostare questi su nil. Se non possono essere nilDB not null+ DB default + github.com/sshaw/keep_defaults sono la strada da percorrere dalla mia esperienza
sshaw

47

Inseriamo i valori predefiniti nel database attraverso le migrazioni (specificando il :default opzione nella definizione di ciascuna colonna) e consentiamo a Record attivo di utilizzare questi valori per impostare il valore predefinito per ciascun attributo.

IMHO, questo approccio è allineato ai principi di AR: convenzione sulla configurazione, DRY, la definizione della tabella guida il modello, non viceversa.

Si noti che i valori predefiniti sono ancora nel codice dell'applicazione (Ruby), sebbene non nel modello ma nelle migrazioni.


3
Un altro problema è quando si desidera un valore predefinito per una chiave esterna. Non è possibile codificare un valore ID nel campo chiave esterna perché su DB diversi l'ID potrebbe essere diverso.
shmichael,

2
un altro problema è che in questo modo non è possibile inizializzare accessori non persistenti (attributi che non sono colonne db).
Viktor Trón,

2
un altro problema è che non puoi necessariamente vedere tutti i valori predefiniti in un unico posto. potrebbero essere sparsi attraverso diverse migrazioni.
declan

8
declan, c'è db / schema.rb
Benjamin Atkin,

6
Vorrei citare per i futuri lettori: almeno da quello che ho letto, questo è contrario ai principi di AR. La logica dei modelli dovrebbe basarsi sulle classi dei modelli e i database dovrebbero essere il più ignoranti possibile. I valori predefiniti per me costituiscono una logica specifica su un modello.
Darethas,

40

Alcuni casi semplici possono essere gestiti definendo un valore predefinito nello schema del database, ma questo non gestisce un numero di casi più complicati, inclusi i valori calcolati e le chiavi di altri modelli. Per questi casi lo faccio:

after_initialize :defaults

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

Ho deciso di utilizzare after_initialize ma non voglio che venga applicato agli oggetti che vengono trovati solo quelli nuovi o creati. Penso che sia quasi scioccante che non venga fornito un callback after_new per questo ovvio caso d'uso, ma l'ho fatto confermando se l'oggetto è già persistito, indicando che non è nuovo.

Dopo aver visto la risposta di Brad Murray questo è ancora più pulito se la condizione viene spostata nella richiesta di richiamata:

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

4
Questo è un punto davvero importante. Devo immaginare che nella maggior parte dei casi l'impostazione del valore predefinito su un record deve essere eseguita solo prima di mantenere un nuovo record, non quando si carica un record persistente.
Russell Silva,

Grazie amico, mi hai salvato la giornata.
Stephanfriedrich,

2
Che ne dici :before_create?
Franklin Yu,

In che modo: before_create gestisce le chiamate nuove e salvate separate? Vorrei verificarlo e capire davvero prima di passare ad esso.
Joseph Lord,

17

Il modello di callback after_initialize può essere migliorato semplicemente facendo quanto segue

after_initialize :some_method_goes_here, :if => :new_record?

Questo ha un vantaggio non banale se il tuo codice di inizializzazione deve avere a che fare con le associazioni, poiché il codice seguente innesca un n + 1 sottile se leggi il record iniziale senza includere quello associato.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

16

I ragazzi di Phusion hanno qualche bel plugin per questo.


Nota, questo plugin consente ai :defaultvalori nelle migrazioni di schemi di "funzionare" Model.new.
jchook,

Posso ottenere i :defaultvalori nelle migrazioni con cui "semplicemente lavorare" Model.new, contrariamente a quanto ha affermato Jeff nel suo post. Lavoro verificato in Rails 4.1.16.
Magne,


8

Uso la attribute-defaultsgemma

Dalla documentazione: esegui sudo gem install attribute-defaultse aggiungi require 'attribute_defaults'alla tua app.

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"

7

Domande simili, ma tutte hanno un contesto leggermente diverso: - Come posso creare un valore predefinito per gli attributi nel modello di Rails activerecord?

Migliore risposta: dipende da cosa vuoi!

Se vuoi che ogni oggetto inizi con un valore: usaafter_initialize :init

Vuoi che il new.htmlmodulo abbia un valore predefinito all'apertura della pagina? utilizzare 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 si desidera che ogni oggetto abbia un valore calcolato dall'input dell'utente: utilizzarebefore_save :default_values Si desidera inserire l'utenteXe quindiY = X+'foo'? uso:

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

4

Ecco a cosa servono i costruttori! Sostituisci il initializemetodo del modello .

Usa il after_initializemetodo


2
Normalmente avresti ragione, ma non dovresti mai ignorare l'inizializzazione in un modello ActiveRecord in quanto potrebbe non essere sempre chiamato. Dovresti usare after_initializeinvece il metodo.
Luke Redpath,

L'uso di default_scope solo per impostare un valore predefinito è Certamente sbagliato. after_initialize è la risposta corretta.
joaomilho,

4

Sup ragazzi, ho finito per fare quanto segue:

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

Funziona come un fascino!


3

Questa è una risposta da molto tempo, ma ho bisogno spesso di valori predefiniti e preferisco non inserirli nel database. Creo una DefaultValuespreoccupazione:

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 poi usalo nei miei modelli in questo modo:

class Widget < ApplicationRecord
  include DefaultValues

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

3

Ho anche visto persone inserirlo nella loro migrazione, ma preferirei vederlo definito nel codice modello.

Esiste un modo canonico per impostare il valore predefinito per i campi nel modello ActiveRecord?

Il modo canonico di Rails, prima di Rails 5, era effettivamente quello di impostarlo nella migrazione e cercare semplicemente db/schema.rbquando si desidera vedere quali valori predefiniti vengono impostati dal DB per qualsiasi modello.

Contrariamente a quanto afferma la risposta di @Jeff Perrin (che è un po 'vecchia), l'approccio alla migrazione applicherà anche il valore predefinito durante l'utilizzo Model.new, a causa della magia di Rails. Lavoro verificato in Rails 4.1.16.

La cosa più semplice è spesso la migliore. Meno debito di conoscenza e potenziali punti di confusione nella base di codice. E "funziona".

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

In alternativa, per la modifica della colonna senza crearne una nuova, eseguire una delle seguenti operazioni:

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

O forse anche meglio:

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

Controlla la guida RoR ufficiale per le opzioni nei metodi di cambio di colonna.

Non null: falseconsente valori NULL nel DB e, come ulteriore vantaggio, si aggiorna anche in modo che tutti i record DB preesistenti precedentemente nulli siano impostati con il valore predefinito anche per questo campo. Se lo desideri, puoi escludere questo parametro nella migrazione, ma l'ho trovato molto utile!

Il modo canonico in Rails 5+ è, come ha detto @Lucas Caton:

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

1

Il problema con le soluzioni after_initialize è che devi aggiungere una after_initialize a ogni singolo oggetto che guardi fuori dal DB, indipendentemente dal fatto che tu acceda a questo attributo o meno. Suggerisco un approccio pigro.

I metodi di attributo (getter) sono ovviamente i metodi stessi, quindi puoi sovrascriverli e fornire un valore predefinito. Qualcosa di simile a:

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

A meno che, come qualcuno ha sottolineato, è necessario eseguire Foo.find_by_status ('ATTIVO'). In tal caso, penso che avresti davvero bisogno di impostare il valore predefinito nei vincoli del tuo database, se il DB lo supporta.


Questa soluzione e l'alternativa proposta non funzionano nel mio caso: ho una gerarchia di classi STI in cui solo una classe ha quell'attributo e la colonna corrispondente che verrà utilizzata nelle condizioni della query DB.
cmoran92,

1

Mi sono imbattuto in problemi nel after_initializedare ActiveModel::MissingAttributeErrorerrori quando si fanno risultati complessi:

per esempio:

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

"cerca" .wherenell'hash delle condizioni

Quindi ho finito per farlo ignorando l'inizializzazione in questo modo:

def initialize
  super
  default_values
end

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

La superchiamata è necessaria per assicurarsi che l'oggetto inizializzi correttamente ActiveRecord::Baseprima di eseguire il mio codice personalizzato, ovvero: default_values


Mi piace. Dovevo farlo def initialize(*); super; default_values; endin Rails 5.2.0. Inoltre, il valore predefinito è disponibile, anche .attributesnell'hash.
spyle,

1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

2
Mmmhh ... all'inizio sembra geniale, ma dopo aver pensato un po ', vedo alcuni problemi. Innanzitutto, tutti i valori predefiniti non sono in un unico punto, ma sparsi nella classe (immagina di cercarli o cambiarli). Secondo e peggio, non puoi mettere, in seguito, un valore nullo (o anche falso!).
Paradoja,

perché dovresti impostare un valore null come predefinito? lo tiri fuori dalla scatola con AR senza fare nulla. Per quanto riguarda false quando si utilizza una colonna booleana, allora hai ragione, questo non è l'approccio migliore.
Mike Breen,

Non posso parlare per altre abitudini di codifica che non ho avuto problemi perché non disperdo i miei getter / setter in un file di classe. Inoltre, qualsiasi editor di testo moderno dovrebbe facilitare la navigazione verso un metodo (shift-cmd-t in textmate).
Mike Breen,

@paradoja - Riprendo ciò, ora vedo dove si rompe usando anche null. Non necessariamente usando null come impostazione predefinita, ma se in realtà si desidera modificare il valore su null ad un certo punto. Buona cattura @paradoja, grazie.
Mike Breen,

Uso questo metodo poiché funziona bene con gli attributi generati dinamicamente.
Dale Campbell,

0

Anche se farlo per l'impostazione dei valori predefiniti è confuso e scomodo nella maggior parte dei casi, è possibile utilizzare :default_scopeanche. Guarda il commento di squil qui .


0

Il metodo after_initialize è obsoleto, utilizzare invece il callback.

after_initialize :defaults

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

tuttavia, usare : default nelle migrazioni è ancora il modo più pulito.


4
In Rails 3: il after_initializemetodo NON è deprecato . In effetti, il callback in stile macro fornito da un esempio di IS è obsoleto . Dettagli: guide.rubyonrails.org/…
Zabba,

0

Ho scoperto che l'utilizzo di un metodo di convalida fornisce un grande controllo sull'impostazione dei valori predefiniti. È anche possibile impostare i valori predefiniti (o fallire la convalida) per gli aggiornamenti. Se lo desideri, puoi anche impostare un valore predefinito diverso per inserti e aggiornamenti. Si noti che il valore predefinito non verrà impostato fino a #valid? è chiamato.

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

Per quanto riguarda la definizione di un metodo after_initialize, potrebbero esserci problemi di prestazioni perché after_initialize viene anche chiamato da ogni oggetto restituito da: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find


la validazione non avviene solo prima del salvataggio? Cosa succede se si desidera mostrare le impostazioni predefinite prima di salvare?
Nurettin,

@nurettin Questo è un buon punto e posso vedere perché a volte lo vorresti, ma l'OP non ha menzionato questo come requisito. Devi decidere tu stesso se vuoi il sovraccarico di impostare le impostazioni predefinite su ogni istanza, anche se non viene salvata. L'alternativa è di mantenere un oggetto fittizio per newriutilizzare l' azione.
Kelvin,

0

Se la colonna sembra essere una colonna di tipo "status" e il tuo modello si presta all'uso di macchine a stati, prendi in considerazione l'uso della gemma aasm , dopodiché puoi semplicemente fare

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

Non inizializza ancora il valore per i record non salvati, ma è un po 'più pulito rispetto al rotolare il tuo con inito qualsiasi altra cosa e raccogli gli altri vantaggi di unasm come ambiti per tutti i tuoi stati.



0

Consiglio vivamente di usare la gemma "default_value_for": https://github.com/FooBarWidget/default_value_for

Ci sono alcuni scenari difficili che richiedono praticamente l'override del metodo di inizializzazione, cosa che fa quella gemma.

Esempi:

Il valore predefinito db è NULL, il valore predefinito del modello / definito da ruby ​​è "una stringa", ma in realtà si desidera impostare il valore su zero per qualsiasi motivo:MyModel.new(my_attr: nil)

La maggior parte delle soluzioni qui non riesce a impostare il valore su zero e lo imposterà invece sul valore predefinito.

OK, quindi invece di adottare l' ||=approccio, passi a my_attr_changed?...

MA ora immaginate che il vostro predefinito db sia "qualche stringa", il vostro default del modello / definito da ruby ​​sia "qualche altra stringa", ma in un certo scenario, volete impostare il valore su "qualche stringa" (il predefinito db):MyModel.new(my_attr: 'some_string')

Questo si tradurrà in my_attr_changed?essere falsa in quanto il valore di default corrisponde al db, che a sua volta licenziare il tuo codice di default rubino definito e impostare il valore a "qualche altra stringa" - ancora una volta, non quello che si desidera.


Per questi motivi, non credo che ciò possa essere realizzato correttamente con un semplice hook after_initialize.

Ancora una volta, penso che la gemma "default_value_for" stia adottando il giusto approccio: https://github.com/FooBarWidget/default_value_for


0

Ecco una soluzione che ho usato che ero un po 'sorpreso non è stata ancora aggiunta.

Ci sono due parti. La prima parte imposta l'impostazione predefinita nella migrazione effettiva e la seconda parte aggiunge una convalida nel modello per garantire che la presenza sia vera.

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

Quindi vedrai che l'impostazione predefinita è già impostata. Ora nella convalida vuoi assicurarti che ci sia sempre un valore per la stringa, quindi fallo

 validates :new_team_signature, presence: true

Ciò che farà sarà impostare il valore predefinito per te. (per me ho "Welcome to the Team"), e poi andrà un passo avanti assicurandomi che sia sempre presente un valore per quell'oggetto.

Spero che aiuti!


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

-2

usa default_scope nei binari 3

api doc

ActiveRecord nasconde la differenza tra il default definito nel database (schema) e il default fatto nell'applicazione (modello). Durante l'inizializzazione, analizza lo schema del database e prende nota di tutti i valori predefiniti ivi specificati. Successivamente, durante la creazione di oggetti, assegna quei valori predefiniti specificati dallo schema senza toccare il database.

discussione


se si utilizza meta_where, default_scope potrebbe non funzionare per l'assegnazione dei valori predefiniti ai nuovi oggetti AR a causa di un bug.
Viktor Trón,

questo problema meta_where è stato risolto [ metautonomous.lighthouseapp.com/projects/53011/tickets/…
Viktor Trón

3
NON usare default_scope. Questo farà sì che tutte le tue domande aggiungano questa condizione al campo che hai impostato. Non è quasi MAI quello che vuoi.
Brad

@brad, divertente mi dici, sono assolutamente d'accordo, è malvagio :). vedi il mio commento in stackoverflow.com/questions/10680845/… .
Viktor Trón,

-3

Dai documenti api http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Utilizza il before_validationmetodo nel tuo modello, ti offre la possibilità di creare un'inizializzazione specifica per creare e aggiornare chiamate, ad esempio in questo esempio (di nuovo codice preso dall'esempio di api docs) il campo numerico è inizializzato per una carta di credito. Puoi facilmente adattarlo per impostare qualunque valore desideri

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

Sorpreso che il suo non sia stato suggerito qui


before_validation non imposterà i valori predefiniti fino a quando l'oggetto non sarà pronto per essere persistito. Se il processo deve leggere i valori predefiniti prima di persistere, i valori non saranno pronti.
mmell,

Non impostare mai i valori predefiniti durante il controllo delle convalide comunque. Non è nemmeno un hack. Fallo durante l'inizializzazione
Sachin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.