ActiveRecord'da varsayılan değerleri nasıl ayarlayabilirim?


417

ActiveRecord'da varsayılan değeri nasıl ayarlayabilirim?

Pratik'ten çirkin, karmaşık bir kod yığınını tanımlayan bir yazı görüyorum: 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

Aşağıdaki örnekleri dolaşarak gördüm:

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

ve

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

İnsanların göçlerine koyduklarını gördüm, ancak model kodunda tanımlanmış olmasını tercih ederim.

ActiveRecord modelindeki alanlar için varsayılan değer belirlemenin standart bir yolu var mı?


Görünüşe göre soruyu iki farklı şekilde kendiniz cevapladınız :)
Adam Byrtek

19
'Self.status' için "standart" Ruby deyim = self.status '' self.status '= ACTIVE' olmadığı sürece AKTİF
Mike Woodhouse

1
Jeff Perrin'in cevabı şu anda kabul edilmiş olarak işaretlenmiş olandan çok daha iyi. default_scope, varsayılan değerleri ayarlamak için kabul edilemez bir çözümdür, çünkü sorguların davranışını da değiştirmenin BÜYÜK YAN ETKİSİ vardır.
lawrence


2
Bu soruya tüm upvotes verildiğinde, Ruby'nin ActiveRecord için bir setDefaultValue yöntemine ihtiyacı olduğunu söyleyebilirim
spartikus

Yanıtlar:


557

Mevcut yöntemlerin her biri ile ilgili birkaç sorun vardır, ancak after_initializegeri arama tanımlamanın aşağıdaki nedenlerden ötürü yol olduğuna inanıyorum :

  1. default_scopeyeni modeller için değerleri başlatır, ancak daha sonra bu modeli bulduğunuz kapsam haline gelir. Bazı sayıları 0 olarak başlatmak istiyorsanız, istediğiniz şey bu değildir .
  2. Taşımanıza varsayılan tanımlanması da zamanlarının bir kısmını çalışır ... Daha önce bu irade söz edilmiştir değil sadece Model.new çağırdığınızda işi.
  3. Geçersiz kılma initializeişe yarayabilir, ancak aramayı unutmayın super!
  4. Phusion's gibi bir eklenti kullanmak biraz saçma oluyor. Bu yakut, gerçekten bazı varsayılan değerleri başlatmak için bir eklentiye ihtiyacımız var mı?
  5. Rails 3'ten geçersiz kılma after_initialize reddedildi . Rails 3.0.3'te geçersiz kıldığımda ,after_initialize konsolda aşağıdaki uyarıyı alıyorum:

DEPRECATION UYARI: after_initialize sonrasındaki Base # kullanımdan kaldırıldı, lütfen bunun yerine Base.after_initialize: yöntemini kullanın. (/ Users / me / myapp / app / models / my_model: 15'den çağrıldı)

Bu nedenle , derneklerde varsayılanları ayarlamanıza ek olarakafter_initialize varsayılan nitelikleri belirlemenize izin veren bir geri arama yazmayı söyleyebilirim :

  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    

Artık modellerinizin başlatılması için tek bir yeriniz var . Birisi daha iyi olana kadar bu yöntemi kullanıyorum.

Uyarılar:

  1. Boole alanları için şunları yapın:

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

    Daha fazla bilgi için Paul Russell'ın bu cevap hakkındaki yorumuna bakın

  2. Bir model için yalnızca bir sütun alt kümesi seçiyorsanız (örneğin; selectgibi bir sorguda kullanmak Person.select(:firstname, :lastname).all) MissingAttributeError, inityönteminiz selectmaddeye dahil edilmemiş bir sütuna erişirse bir alırsınız . Bu davaya şu şekilde karşı çıkabilirsiniz:

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

    ve bir boole sütunu için ...

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

    Ayrıca sözdiziminin Rails 3.2'den önce farklı olduğuna dikkat edin (aşağıdaki Cliff Darling'in yorumuna bakın)


7
Bu kesinlikle bunu başarmanın en iyi yolu gibi görünüyor. Bu gerçekten garip ve talihsiz. Oluşturma üzerine model özniteliği varsayılanları oluşturmak için mantıklı bir tercih edilen yöntem, Rails'in zaten inşa etmesi gereken bir şey gibi görünüyor. Geçersiz kılan tek (güvenilir) yol initialize, sadece açık ve iyi tanımlanmış bir şey için gerçekten kıvrılmış görünüyor. Bu işlevsellik zaten orada bir yerde olduğunu varsaydım ve ben sadece farkında değildi çünkü burada arama önce belgeleri tarama saat geçirdim.
seaneshbaugh

106
Bu konuda bir not - varsayılan olarak ayarlamak istediğiniz bir boole alanınız varsa, bunu yapmayın self.bool_field ||= true, çünkü bu alanı açıkça false olarak ayarlasanız bile alanı true değerine zorlar. Bunun yerine yapın self.bool_field = true if self.bool_field.nil?.
Paul Russell

2
2. nokta ile ilgili olarak, Model.new aslında (yalnızca benim için?) Taşımalarda tanımlanan varsayılanlarla veya daha doğrusu tablo sütunları için varsayılan değerlerle çalışır. Ancak Jeff'in after_initialize geri çağrıya dayanan yönteminin muhtemelen en iyi yol olduğunu kabul ediyorum. Sadece bir soru: kirli ama kaydedilmemiş nesnelerle çalışıyor mu? Örneğinizde, Person.new.number_was 0,0 döndürecek mi?
Laurent Farcy

21
Bu yaklaşımı, aktif kaydı olan belirli sütunları seçmeyle birlikte kullanırken dikkatli olun. Bu durumda, yalnızca sorguda belirtilen öznitelikler nesnede bulunur ve init kodu a atar MissingAttributeError. Sen gösterildiği gibi ek bir sınama ekleyebilirsiniz: self.number ||= 0.0 if self.has_attribute? :number boolelerde için: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Bu Rails 3.2+ - daha önce kullanmak için self.attributes.has_key?ve sembol yerine bir dizeye ihtiyacınız var.
Cliff Darling

6
Bunu derneklerle yapmak, bu dernekleri aramaya yükler. Performans sorunlarını önlemek için initializeile başlayın return if !new_record?.
Kyle Macey

68

Raylar 5+

Sen kullanabilirsiniz özellik örneğin sizin modelleri içinde yöntemini .:

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

defaultParametreye bir lambda da iletebilirsiniz . Misal:

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

3
ahhhhh bu aradığım mücevher olduğunu! default da bir proc alabilir, örneğin default: -> {Time.current.to_date}
schpet

1
Türü ikinci argüman olarak belirttiğinizden emin olun, aksi takdirde tür olur Valueve hiçbir tiplendirme yapılmaz.
null

benim zevkime göre, bu ayrıca store_accessor ile çalışır, örneğin store_accessor :my_jsonb_column, :localedaha sonra tanımlayabilirsiniz verilenattribute :locale, :string, default: 'en'
Ryan Romanchuk

Oh bu harika, bir formda göstermek için varsayılanlara ihtiyacım vardı ve bu harika çalışıyor. Teşekkürler Lucas.
Paul Watson

Bunları yine de ayarlayabilirsiniz nil. Onlar olamıyorsan nilDB not null+ DB varsayılan + github.com/sshaw/keep_defaults benim deneyimlerinden gitmek yoludur
sshaw

47

Veritabanına varsayılan değerleri geçişler yoluyla koyarız ( :defaulther sütun tanımındaki seçeneği belirterek ) ve Active Record'un her bir öznitelik için varsayılan değeri ayarlamak üzere bu değerleri kullanmasına izin veririz .

IMHO, bu yaklaşım AR ilkeleri ile uyumludur: konfigürasyon üzerinde konvansiyon, DRY, tablo tanımı modeli yönlendirir, tersi değil.

Varsayılanların hala uygulamada (Ruby) kodda olduğunu, ancak modelde değil, taşımada olduğunu unutmayın.


3
Başka bir sorun, yabancı anahtar için varsayılan bir değer istemenizdir. Farklı DB'lerde kimlik farklı olabileceğinden, kimlik değerini yabancı anahtar alanına sabit olarak kodlayamazsınız.
shmichael

2
yine başka bir sorun, kalıcı olmayan erişimcileri (db sütun olmayan öznitelikler) başlatamazsınız olmasıdır.
Viktor Trón

2
başka bir sorun, tüm varsayılan değerleri tek bir yerde görememenizdir. farklı göçlerle dağılmış olabilirler.
declan

8
declan, orada db / schema.rb
Benjamin Atkin

6
Gelecekteki okuyucular için bahsetmek istiyorum: en azından okuduğumdan, bu AR ilkelerine aykırı. Modeller için mantık, model sınıfları içinde olmalıdır ve veritabanları olabildiğince cahil olmalıdır. Benim için varsayılan değerler bir model hakkında belirli bir mantık oluşturur.
darethas

40

Bazı basit durumlar, veritabanı şemasında bir varsayılan tanımlanarak ele alınabilir, ancak bu, hesaplanmış değerler ve diğer modellerin anahtarları dahil olmak üzere bir dizi daha zor durumla başa çıkmaz. Bu durumlar için bunu yaparım:

after_initialize :defaults

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

After_initialize kullanmaya karar verdim ancak yalnızca yeni veya yaratılan nesnelere uygulanmasını istemiyorum. Sanırım bu bariz kullanım durumu için bir after_new geri çağırma sağlanmadığı şok edici olduğunu, ancak nesnenin zaten yeni olup olmadığını gösteren kalıcı olup olmadığını onaylayarak yaptım.

Brad Murray'ın cevabını gördükten sonra, durum geri arama isteğine taşındığında bu daha da temizdir:

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
Bu gerçekten önemli bir nokta. Çoğu durumda, bir kaydın varsayılanını ayarlamanın, kalıcı bir kayıt yüklenirken değil, yalnızca yeni bir kayda devam etmeden önce yapılması gerektiğini hayal etmeliyim.
Russell Silva

Teşekkürler dostum, Günümü kurtardın.
stephanfriedrich

2
Nasıl :before_create?
Franklin Yu

Before_create nasıl yeni ve ayrı çağrıları ele alır? Geçiş yapmadan önce bunu kontrol etmek ve gerçekten anlamak istiyorum.
Joseph Lord

17

After_initialize geri arama kalıbı basitçe aşağıdakileri yaparak geliştirilebilir

after_initialize :some_method_goes_here, :if => :new_record?

Başlangıç ​​kodunuzun ilişkilendirmelerle uğraşması gerekiyorsa, bunun önemsiz bir yararı vardır, çünkü aşağıdaki kod, ilişkilendirmeyi içermeden ilk kaydı okursanız, ince bir n + 1'i tetikler.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

16

Phusion çocuklar bunun için güzel bir eklenti var .


Bu eklenti, :defaultşema taşımalarındaki değerlerin 'sadece çalışmasına' izin verir Model.new.
jchook

Alabilirim :defaultile 'sadece iş' için göçler değerleri Model.newJeff görevinden söylediklerime, tam tersine. Raylarda çalıştığı doğrulanmıştır 4.1.16.
Magne


8

Kullandığım attribute-defaultsmücevher

Belgelerden: çalıştırın sudo gem install attribute-defaultsve require 'attribute_defaults'uygulamanıza ekleyin .

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

Benzer sorular, ancak hepsinin biraz farklı bağlamları var: - Rails activerecord'un modelindeki özellikler için nasıl varsayılan bir değer oluştururum?

En İyi Yanıt: Ne İstediğinize Bağlıdır!

İsterseniz her nesne için bir değerle başlar: kullanılmasınıafter_initialize :init

new.htmlSayfayı açtıktan sonra formun varsayılan bir değere sahip olmasını ister misiniz? https://stackoverflow.com/a/5127684/1536309 kullanın

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 

İsterseniz her nesne için bir değer kullanıcı girişi hesaplanan adres: kullanımbefore_save :default_values Kullanıcı girmek isteyenXsonra veY = X+'foo'? kullanın:

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

4

Bu inşaatçılar içindir! Modelin initializeyöntemini geçersiz kılın .

after_initializeYöntemi kullanın .


2
Normalde doğru olursunuz, ancak ActiveRecord modelinde başlatmayı hiçbir zaman geçersiz kılmamalısınız, çünkü her zaman çağrılmayabilir. Bunun after_initializeyerine yöntemi kullanmalısınız.
Luke Redpath

Bir default_scope kullanmak, sadece bir varsayılan ayarlamak için KESİNLİKLE yanlış. after_initialize doğru cevaptır.
joaomilho

4

Sup guys, ben aşağıdakileri yaparak sona erdi:

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

Tıkır tıkır çalışıyor!


3

Bu uzun bir süre cevaplandı, ancak sık sık varsayılan değerlere ihtiyacım var ve bunları veritabanına koymamayı tercih ediyorum. Bir DefaultValuesendişe yaratıyorum:

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

Ve sonra böyle modellerimde kullanın:

class Widget < ApplicationRecord
  include DefaultValues

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

3

İnsanların göçlerine koyduklarını gördüm, ancak model kodunda tanımlanmış olmasını tercih ederim.

ActiveRecord modelindeki alanlar için varsayılan değer belirlemenin standart bir yolu var mı?

Kanunlar Rails yolu, Rails 5'ten önce, onu db/schema.rbgeçişte ayarlamaktı ve herhangi bir model için DB tarafından hangi varsayılan değerlerin ayarlandığını görmek istediğinizde sadece bakın .

@Jeff Perrin yanıtının (biraz eski) yanıtının aksine Model.new, bazı Rails sihri nedeniyle geçiş yaklaşımı kullanıldığında varsayılanı bile uygular . Raylarda çalıştığı doğrulanmıştır 4.1.16.

En basit şey genellikle en iyisidir. Daha az bilgi borcu ve kod tabanındaki potansiyel karışıklık noktaları. Ve bu sadece işe yarıyor.

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

Veya yeni bir sütun oluşturmadan sütun değiştirmek için aşağıdakilerden birini yapın:

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

Ya da belki daha da iyisi:

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

Resmi RoR kılavuzunu kontrol edinSütun değiştirme yöntemlerindeki seçenekler bakın.

null: falseOlanak vermez bir fayda ilave olarak, aynı zamanda boş önceden hepsi önceden varolan DB kayıtları da bu alan için varsayılan değeri ile ayarlanır, böylece günceller DB değerleri NULL, vb. İsterseniz bu parametreyi taşıma işleminde hariç tutabilirsiniz, ancak çok kullanışlı buldum!

@Lucas Caton'un dediği gibi Rails 5+'daki kanonik yol:

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

1

After_initialize çözümleriyle ilgili sorun, bu özelliğe erişip erişmediğinize bakılmaksızın, DB'den baktığınız her bir nesneye after_initialize eklemeniz gerektiğidir. Tembel yüklü bir yaklaşım öneriyorum.

Öznitelik yöntemleri (getters) elbette yöntemlerin kendisidir, böylece bunları geçersiz kılabilir ve bir varsayılan sağlayabilirsiniz. Gibi bir şey:

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

Birisi işaret ettiği gibi, Foo.find_by_status ('ACTIVE') yapmalısınız. Bu durumda, DB destekliyorsa, veritabanı kısıtlamalarınızda varsayılanı gerçekten ayarlamanız gerektiğini düşünüyorum.


Bu çözüm ve önerilen alternatif benim durumumda çalışmıyor: Ben sadece bir sınıfın bu özniteliği ve DB sorgu koşullarında kullanılacak karşılık gelen sütun bir STI sınıfı hiyerarşisi var.
cmoran92

1

Karmaşık bulgular yaparken hata after_initializevermekle ilgili problemlerle karşılaştım ActiveModel::MissingAttributeError:

Örneğin:

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

.wherekoşulların karmasındaki "arama"

Bu yüzden başlatmayı bu şekilde geçersiz kılarak yaptım:

def initialize
  super
  default_values
end

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

superÇağrı emin nesne itibaren doğru başlatılıyor yapmak için gerekli olan ActiveRecord::Baseyani benim özelleştir kodunu yapmadan önce: default_values


Bunu sevdim. def initialize(*); super; default_values; endRails 5.2.0'da yapmam gerekiyordu . Ayrıca, .attributeskarma değerde bile varsayılan değer kullanılabilir .
2018 spyle

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

  before_save{ self.status ||= ACTIVE }
end

2
Mmmhh ... başlangıçta ustaca görünüyor, ama biraz düşündükten sonra birkaç sorun görüyorum. İlk olarak, tüm varsayılan değerler tek bir noktada değildir, ancak sınıf boyunca dağılmıştır (bunları aramayı veya değiştirmeyi hayal edin). İkincisi ve en kötüsü, daha sonra boş bir değer (veya yanlış bir değer bile!) Koyamazsınız.
paradoja

neden varsayılan olarak bir boş değer ayarlamanız gerekiyor? bunu hiçbir şey yapmadan AR ile kutudan çıkarırsınız. Boolean sütunu kullanırken false için, o zaman haklısın, bu en iyi yaklaşım değil.
Mike Breen

Alışkanlıkları kodlayan alışkanlıkları kodlayamıyorum Bir sorun yaşamadım çünkü alıcılarımı / seterlerimi bir sınıf dosyası etrafında dağıtmıyorum. Ayrıca, herhangi bir modern metin editörü bir yönteme (textmate'de shift-cmd-t) gitmeyi kolaylaştırmalıdır.
Mike Breen

@paradoja - Onu geri alıyorum, şimdi de null kullanarak nerede bozulduğunu görüyorum. Varsayılan olarak null kullanılması gerekmez, ancak değeri bir noktada null olarak değiştirmek istiyorsanız. İyi yakalama @paradoja, teşekkürler.
Mike Breen

Dinamik olarak oluşturulan özniteliklerle iyi çalıştığı için bu yöntemi kullanıyorum.
Dale Campbell


0

after_initialize yöntemi kullanımdan kaldırıldığında, bunun yerine geri aramayı kullanın.

after_initialize :defaults

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

ancak, taşıma işlemlerinizde : varsayılan özelliğini kullanmak en temiz yoldur.


4
Raylar 3'te: after_initializeyöntem olduğunu kaldırılmış DEĞİL . Aslında, IS örneğini verdiğiniz makro tarzı geri arama kullanımdan kaldırıldı . Ayrıntılar: guides.rubyonrails.org/…
Zabba

0

Bir doğrulama yöntemi kullanarak varsayılanları ayarlama üzerinde çok fazla kontrol sağlar buldum. Güncellemeler için varsayılanları bile ayarlayabilir (veya doğrulamayı geçemez). Gerçekten isterseniz, ekler ve güncellemeler için farklı bir varsayılan değer bile ayarlayabilirsiniz. Varsayılan ayar # geçersiz olana kadar ayarlanmayacak mı? denir.

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

After_initialize yönteminin tanımlanması ile ilgili olarak, performans sorunları olabilir çünkü after_initialize, döndürülen her nesne tarafından da çağrılır: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find


doğrulama yalnızca kaydetmeden önce gerçekleşmez mi? Kaydetmeden önce varsayılanları göstermek isterseniz ne olur?
nurettin

@nurettin Bu iyi bir nokta ve bunu neden bazen istediğinizi görebiliyorum, ama OP bunu bir gereklilik olarak belirtmedi. Kaydedilmemiş olsa bile, her örnekte varsayılanları ayarlama yükünü isteyip istemediğinize kendiniz karar vermelisiniz. Alternatif, neweylemin yeniden kullanılması için kukla bir nesne bulundurmaktır .
Kelvin

0

Sütun 'durum' tipi bir sütunsa ve modeliniz kendini devlet makinelerinin kullanımına borçluysa , aasm gemini kullanmayı düşünün , ardından bunu yapabilirsiniz.

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

Kaydedilmemiş kayıtların değerini hala başlatmaz, ancak kendinizle initveya herhangi bir şeyle yuvarlanmaktan biraz daha temizdir ve tüm durumlarınız için kapsamlar gibi aasmın diğer avantajlarını elde edersiniz.



0

Kesinlikle "default_value_for" gem kullanmanızı öneririz: https://github.com/FooBarWidget/default_value_for

Bu gem'in yaptığı başlatma yöntemini geçersiz kılmayı gerektiren bazı zor senaryolar vardır.

Örnekler:

Db varsayılan ayarınız NULL, model / ruby ​​tanımlı varsayılan ayarınız "bazı dize" dir, ancak aslında değeri herhangi bir nedenle nil olarak ayarlamak istersiniz :MyModel.new(my_attr: nil)

Buradaki çoğu çözüm değeri nil olarak ayarlayamaz ve bunun yerine varsayılan olarak ayarlar.

Tamam, bu yüzden ||=yaklaşımı benimsemek yerine my_attr_changed?...

AMA artık db varsayılanınızın "bazı dize" olduğunu, model / ruby ​​tanımlı varsayılanınızın "başka bir dize" olduğunu, ancak belirli bir senaryo altında , değeri "bazı dize" (db varsayılanı) olarak ayarlamak istediğinizi düşünün :MyModel.new(my_attr: 'some_string')

Bu neden olur my_attr_changed?olmanın yanlış değer sırayla yakut tanımlı varsayılan kodu ateş edeceği ve "diğer bazı dize" değer kümesi db varsayılan, eşleştiği için - yine, istediğiniz gibi değil.


Bu nedenlerle bunun bir after_initialize kanca ile düzgün bir şekilde gerçekleştirilebileceğini düşünmüyorum.

Yine, "default_value_for" gem doğru yaklaşımı düşünüyorum: https://github.com/FooBarWidget/default_value_for


0

İşte henüz eklenmemiş biraz şaşırdım bir çözüm.

Bunun iki kısmı var. İlk bölüm, gerçek geçişte varsayılanı ayarlamaktır ve ikinci bölüm, varlığın doğru olduğundan emin olmak için modelde bir doğrulama eklemektedir.

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

Burada varsayılanın zaten ayarlanmış olduğunu göreceksiniz. Şimdi doğrulamada, dize için her zaman bir değer olduğundan emin olmak istiyorsunuz, bu yüzden

 validates :new_team_signature, presence: true

Bunun yapacağı şey sizin için varsayılan değeri ayarlamaktır. (benim için "Takıma Hoş Geldiniz") ve daha sonra bu nesne için her zaman bir değer bulunduğundan emin olmak için bir adım daha ileri gidecektir.

Umarım yardımcı olur!


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

ray 3'te default_scope kullanın

api doc

ActiveRecord, veritabanında (şema) tanımlanan varsayılan ile uygulamada (model) yapılan varsayılan ayar arasındaki farkı gizler. Başlatma sırasında, veritabanı şemasını ayrıştırır ve burada belirtilen varsayılan değerleri not eder. Daha sonra, nesneler oluştururken, şema tarafından belirtilen bu varsayılan değerleri veritabanına dokunmadan atar.

tartışma


meta_where kullanırsanız, default_scope bir hata nedeniyle yeni AR nesnelerine varsayılanlar atamak için çalışmayabilir.
Viktor Trón


3
KULLANMAYIN default_scope. Bu, tüm sorgularınızın bu koşulu ayarladığınız alana eklemesini sağlar. Neredeyse ASLA ne istiyorsan.
brad

@brad, bahsettiğin komik, tamamen katılıyorum, kötü :). benim yorumu görmek stackoverflow.com/questions/10680845/... .
Viktor Trón

-3

Api docs adresinden http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.htmlbefore_validation Modelinizdeki yöntemi kullanın, örneğin bu örnekte çağrı oluşturma ve güncelleme için özel başlatma oluşturma seçenekleri sunar (yine kod alınır) api docs örneğinden) sayı alanı kredi kartı için başlatılır. İstediğiniz değerleri ayarlamak için bunu kolayca uyarlayabilirsiniz

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

Burada önerilmediğini şaşırttı


before_validation, nesne kalıcı olmaya hazır olana kadar varsayılanları ayarlamaz. İşlemin devam etmeden önce varsayılan değerleri okuması gerekiyorsa değerler hazır olmaz.
mmell

Doğrulama denetimi sırasında hiçbir zaman varsayılan değerleri ayarlamazsınız. Herhangi bir Hack bile değil. Başlatma sırasında yapın
Sachin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.