Rails'de varsayılan değerler nasıl ayarlanır?


108

Rails'te nesneler için varsayılan değerleri ayarlamanın en iyi yolunu bulmaya çalışıyorum.

Aklıma gelen en iyi şey new, denetleyicideki yöntemde varsayılan değeri ayarlamaktır .

Bu kabul edilebilirse veya bunu yapmanın daha iyi bir yolu varsa, herhangi bir girdisi olan var mı?


1
Bu nesneler nelerdir; nasıl tüketilir / kullanılır? Görünümleri işlerken mi yoksa denetleyici mantığı için mi kullanılıyorlar?
Gishu

3
Bir ActiveRecord nesnesinden bahsediyorsanız, 'varsayılan değerler' sorununun mantıklı bir çözümü olmadığını söylemeliyim. Sadece çılgın hackler ve rails yazarları bu özelliğin buna değdiğini düşünmüyor gibi görünüyor (sadece raylar topluluğu olduğu için inanılmaz)
Mauricio

Kabul edilen ve çoğu yanıt ActiveRecords'a odaklandığından, asıl sorunun AcitveRecords ile ilgili olduğunu varsayıyoruz. Bu nedenle stackoverflow.com/questions/328525/…
Ciro Santilli of 冠状 病 六四 事件 法轮功

Yanıtlar:


98

Ruby'de "Doğru" tehlikeli bir kelimedir. Bir şeyi yapmanın genellikle birden fazla yolu vardır. Bu tablodaki o sütun için her zaman varsayılan değeri isteyeceğinizi biliyorsanız , bunları bir DB geçiş dosyasında ayarlamak en kolay yoldur:

class SetDefault < ActiveRecord::Migration
  def self.up
    change_column :people, :last_name, :type, :default => "Doe"
  end

  def self.down
    # You can't currently remove default values in Rails
    raise ActiveRecord::IrreversibleMigration, "Can't remove the default"
  end
end

ActiveRecord tablo ve sütun özelliklerinizi otomatik olarak keşfettiğinden, bu, herhangi bir standart Rails uygulamasında onu kullanan herhangi bir modelde aynı varsayılanın ayarlanmasına neden olur.

Ancak, yalnızca belirli durumlarda varsayılan değerlerin ayarlanmasını istiyorsanız - örneğin, bu, bir tabloyu başkalarıyla paylaşan miras alınan bir modeldir - o zaman başka bir zarif yol, model nesnesi oluşturulduğunda bunu doğrudan Rails kodunuzda yapmaktır:

class GenericPerson < Person
  def initialize(attributes=nil)
    attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
    super(attr_with_defaults)
  end
end

Sonra, bir yaptığınızda GenericPerson.new(), Person.new()siz onu başka bir şeyle geçersiz kılmadığınız sürece "Doe" özniteliğini her zaman damlatır .


2
Dene. .newSınıf yöntemiyle çağrılan yeni model nesneler üzerinde çalışacaktır . Bu blog gönderisinin ActiveRecord'u doğrudan çağırmayla ilgili tartışması .allocate, veritabanından mevcut verilerle yüklenen model nesneler hakkındaydı. ( Ve ActiveRecord'un bu şekilde çalışması korkunç bir fikir, IMO. Ama konu bu değil.)
SFEley

2
Orijinal posterin sorusunu tekrar okumak için geri adım atarsanız, Nikita ve ardından yorumlarımı sırayla okursanız, bu size daha anlamlı gelebilir. Değilse ... Pekala, soru cevaplandı. İyi günler.
SFEley

8
Aşağı geçiş için daha iyi: change_column_default :people, :last_name, nil stackoverflow.com/a/1746246/483520
Nolan Amy

9
Daha yeni ray versiyonları için, varsayılan parametreden önce ek bir tip parametresine ihtiyacınız olduğunu lütfen unutmayın. Ara: bu sayfada yazın.
Ben Wheeler

3
@JoelBrewer Bugün bununla karşılaştım - Rails 4 için sütun türünü de belirtmeniz gerekiyor:change_column :people, :last_name, :string, default: "Doe"
GoBusto

56

SFEley'in cevabına göre, işte yeni Rails sürümleri için güncellenmiş / düzeltilmiş bir tane:

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end

2
Bunun Rails 4.2.x'te çalışmadığına dikkat edin (sonraki sürümlerde test edilmemiştir). olarak change_columnoldukça "yapılandıran" olabilir, ters işlem çıkaramayız. Eğer emin değilseniz, sadece çalıştırarak test db:migrateve db:rollbackhemen sonra. Cevap aynı sonuç olarak kabul edildi, ancak en azından varsayıldı!
gfd

1
Bu O raylar 5.1 ile çalışır ancak bunu gerekecektir .. benim için doğru cevaptır upve down@GoBusto yukarıda anlatılan gibi
Fabrizio BERTOGLIO

22

Her şeyden önce aşırı yüklenemezsin initialize(*args) , her durumda çağrılmadığı için .

En iyi seçeneğiniz, varsayılanlarınızı geçişinize koymaktır:

add_column :accounts, :max_users, :integer, :default => 10

İkinci en iyi şey, varsayılanları modelinize yerleştirmektir, ancak bu yalnızca başlangıçta sıfır olan özniteliklerle çalışacaktır. booleanSütunlarla yaptığım gibi sorun yaşayabilirsiniz :

def after_initialize
  if new_record?
    max_users ||= 10
  end
end

İhtiyacın var new_record? datbase yüklenen varsayılan geçersiz kılmaz, böylece değerleri.

||=Rails'in başlatma yöntemine geçirilen parametreleri geçersiz kılmasını durdurmanız gerekir .


12
küçük not - iki şey yapmak istiyorsunuz: .... 1) after_initialize yönteminizi çağırmayın. İstersen after_initiation :your_method_name.... 2 kullanself.max_users ||= 10
Jesse Wolgamott

5
Boole'lar için şunu yapın: prop = true ise prop.nil?
Francis Potter

2
veya after_initialize doyerinedef after_initialize
fotanus

self.max_users güvenli olmak için.
Ken Ratanachai S.

15

Ayrıca change_column_defaultgeçişlerinizi de deneyebilirsiniz (Rails 3.2.8'de test edilmiştir):

class SetDefault < ActiveRecord::Migration
  def up
    # Set default value
    change_column_default :people, :last_name, "Smith"
  end

  def down
    # Remove default
    change_column_default :people, :last_name, nil
  end
end

change_column_default Rails API belgeleri


1
Kabul edilen cevap daha eksiksiz ama ben bu temiz ve belgelenmiş cevabı beğendim ... Teşekkürler!
gfd

7

ActiveRecord nesnelerine atıfta bulunuyorsanız, bunu yapmanın (birden fazla) yolu vardır:

1. Veritabanında a: varsayılan parametre kullanın

ÖRNEĞİN

class AddSsl < ActiveRecord::Migration
  def self.up
    add_column :accounts, :ssl_enabled, :boolean, :default => true
  end

  def self.down
    remove_column :accounts, :ssl_enabled
  end
end

Burada daha fazla bilgi: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Bir geri arama kullanın

ÖRNEĞİN before_validation_on_create

Daha fazla bilgi burada: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147


2
Veritabanında varsayılanları kullanmanın sorunu, nesne kaydedilene kadar ayarlanamayacakları değil mi? Varsayılanları doldurulmuş yeni bir nesne oluşturmak istersem, bunları başlatıcıda ayarlamam gerekir.
Rafe

Booleanlar varsayılan olarak 1 veya 0 olamaz - doğru veya yanlış olarak ayarlanmaları gerekir (Silasj'ın cevabına bakın).
Jamon Holmgren

@JamonHolmgren Yorum için teşekkürler, cevabı düzelttim :)
Vlad Zloteanu

Sorun değil @VladZloteanu
Jamon Holmgren

7

Ruby on Rails v3.2.8'de, after_initializeActiveRecord geri aramasını kullanarak, modelinizde yeni bir nesne için varsayılan değerleri atayacak bir yöntem çağırabilirsiniz.

after_initialize geri araması, bir bulucu tarafından bulunan ve başlatılan her nesne için tetiklenir; after_initialize, yeni nesneler de başlatıldıktan sonra tetiklenir ( bkz. ActiveRecord Geri Çağırmaları ).

Bu nedenle, IMO şuna benzer şekilde görünmelidir:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    # required to check an attribute for existence to weed out existing records
    self.bar = default_value unless self.attribute_whose_presence_has_been_validated
  end
end

Foo.bar = default_valueörnek attribute_whose_presence_has_been_validateddaha önce kaydetme / güncelleme işlemi içermediği sürece bu örnek için . Ardından default_value, default_valuefor barözelliğini kullanarak formu oluşturmak için görünümünüzle birlikte kullanılır .

En iyi ihtimalle bu hile ...

DÜZENLE - 'new_record?' yeni bir aramadan örnek oluşturup oluşturmadığını kontrol etmek için

Bir öznitelik değerini kontrol etmek yerine, new_record?raylarla yerleşik yöntemi kullanın . Yani yukarıdaki örnek şöyle görünmelidir:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo, if: 'new_record?'
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    self.bar = default_value
  end
end

Bu çok daha temiz. Ah, Rails'in büyüsü - benden daha akıllı.


Bu beklediğim gibi çalışmıyor - varsayılanı yalnızca yeniye ayarlaması gerekiyordu, ancak aynı zamanda bulunan ve başlatılan her nesnenin özniteliğine varsayılanı da ayarlıyor (yani, db'den yüklenen kayıtlar). Varsayılanı atamadan önce değerleri arayan bir nesne niteliğini kontrol edebilirsiniz, ancak bu çok zarif veya sağlam bir çözüm değildir.
erroric

1
Neden after_initializegeri aramayı burada tavsiye ediyorsunuz ? Geri aramalarla ilgili Rails dokümantasyonu, before_createfazladan koşul kontrolleri olmadan varsayılan değeri ayarlama örneğini içerir
Dfr

@Dfr - Bunu kaçırmış olmalıyım, gözden geçirmem için bana bir bağlantı atar mısın ve cevabı güncelleyeceğim ...
erroric

Evet, burada api.rubyonrails.org/classes/ActiveRecord/Callbacks.html ilk örnek, sınıfSubscription
Dfr

5
@Dfr - Geri arama after_initializeyerine kullanmamızın nedeni, yeni nesneler oluştururken kullanıcı için (bir görünümde kullanım before_createiçin) varsayılan bir değer ayarlamak istememizdir . Geri arama, kullanıcıya yeni bir nesne verildikten , girdisi sağlandıktan ve nesneyi oluşturulmak üzere denetleyiciye gönderildikten sonra çağrılır . Denetleyici daha sonra herhangi bir geri arama olup olmadığını kontrol eder . Karşıt-sezgisel görünüyor, ancak bu bir isimlendirme meselesi - eylemi ifade ediyor . Yeni bir nesneyi örneklemek nesne değildir . before_createbefore_createbefore_createcreatecreate
erroric

5

En azından Rails 3.2.6'daki boole alanları için bu, geçişinizde işe yarayacaktır.

def change
  add_column :users, :eula_accepted, :boolean, default: false
end

Bir boole alanı olduğundan, varsayılan olarak a 1veya koymak 0burada çalışmayacaktır. Bir trueveya falsedeğer olmalıdır .



2

Bir geçiş oluşturun ve kullanın change_column_default, kısa ve öz ve geri döndürülebilir:

class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
  def change
    change_column_default :people, :age, { from: nil, to: 0 }
  end
end

1

Veritabanı destekli bir modelin belirli öznitelikleri için varsayılanları ayarlıyorsanız, sql varsayılan sütun değerlerini kullanmayı düşünürdüm - hangi tür varsayılanları kullandığınızı açıklayabilir misiniz?

Bunu halletmek için birkaç yaklaşım var, bu eklenti ilginç bir seçenek gibi görünüyor.


1
Varsayılanlar ve kısıtlamalarla başa çıkmak için veritabanına güvenmemeniz gerektiğini düşünüyorum, tüm bu şeyler model katmanında sıralanmalıdır. Bu eklenti yalnızca ActiveRecord modelleri için çalışır, nesneler için varsayılanları ayarlamanın genel bir yolu değildir.
Lukas Stejskal

Kullanmaya çalıştığınız varsayılan türlerine bağlı olduğunu iddia ediyorum, bu çok sık yapmam gereken bir şey değildi, ancak kısıtlamaların aslında hem veritabanında hem de model - verilerinizin veritabanında geçersiz olmasını önlemek için.
paulthenerd

1

Yeni / ilklendirmeyi geçersiz kılma önerisi muhtemelen eksiktir. Rails (sıklıkla) ActiveRecord nesneleri için ayırmayı çağırır ve ayırmak için çağrılar başlatılacak çağrılarla sonuçlanmaz.

ActiveRecord nesnelerinden bahsediyorsanız, after_initialize'ı geçersiz kılmaya bir göz atın.

Bu blog gönderileri (benim değil) faydalıdır:

Varsayılan değerler Varsayılan kurucular çağrılmaz

[Düzenleme: SFEley, Rails'in bellekte yeni bir nesne başlatırken veritabanındaki varsayılana baktığına işaret ediyor - bunu fark etmemiştim.]


1

Tıpkı DB'de varsayılan sütun değeri olarak belirtilmiş gibi bir varsayılan ayarlamam gerekiyordu. Yani böyle davranıyor

a = Item.new
a.published_at # => my default value

a = Item.new(:published_at => nil)
a.published_at # => nil

After_initialize callback, bağımsız değişkenlerden öznitelikler ayarlandıktan sonra çağrıldığından, özniteliğin sıfır olup olmadığını bilmenin bir yolu yoktu, çünkü hiç ayarlanmadı veya kasıtlı olarak sıfır olarak ayarlandı. Bu yüzden biraz içeri girmem gerekti ve bu basit çözümle geldim.

class Item < ActiveRecord::Base
  def self.column_defaults
    super.merge('published_at' => Time.now)
  end
end

Benim için harika çalışıyor. (Raylar 3.2.x)



0

Burada benzer bir soruyu yanıtladım .. bunu yapmanın temiz bir yolu attr_accessor_with_default Rails kullanmaktır

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

GÜNCELLEME

attr_accessor_with_default , Rails 3.2'de kullanımdan kaldırıldı .. bunun yerine saf Ruby ile bunu yapabilirsiniz

class SOF
  attr_writer :is_awesome

  def is_awesome
    @is_awesome ||= true
  end
end

sof = SOF.new
sof.is_awesome

#=> true

attr_accessor_with_defaultraylar> 3.1.0 itibariyle kullanımdan kaldırıldı
flynfish

Örneğinizde, is_awesome @is_awesome == false olduğunda bile her zaman doğru olacaktır.
Ritchie



-3

ActiveRecord modeli için yapıcıyı geçersiz kılabilirsiniz.

Bunun gibi:

def initialize(*args)
  super(*args)
  self.attribute_that_needs_default_value ||= default_value
  self.attribute_that_needs_another_default_value ||= another_default_value
  #ad nauseum
end

2
Bu, çekirdek rayların işlevselliğini değiştirmek için korkunç bir yer. Diğer şeyleri bozma olasılığı daha düşük olan, diğer yanıtlarda bahsedilen bunu yapmanın yolları çok daha istikrarlı. Maymun yama sadece başka şekilde yapılamayacak şeyler için son çare olmalıdır.
Will
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.