मैं ActiveRecord में डिफ़ॉल्ट मान कैसे सेट कर सकता हूं?


417

मैं ActiveRecord में डिफ़ॉल्ट मान कैसे सेट कर सकता हूं?

मैं प्रतीक की एक पोस्ट देखता हूं जिसमें कोड का एक बदसूरत, जटिल हिस्सा बताया गया है: 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

मैंने निम्नलिखित उदाहरणों को गुगली करते हुए देखा है:

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

तथा

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

मैंने यह भी देखा है कि लोग इसे अपने माइग्रेशन में रखते हैं, लेकिन मैं इसे मॉडल कोड में परिभाषित करता हूँ।

ActiveRecord मॉडल में फ़ील्ड के लिए डिफ़ॉल्ट मान सेट करने के लिए एक विहित तरीका है?


लगता है कि आपने स्वयं प्रश्न का उत्तर दो अलग-अलग प्रकारों में दिया :)
एडम बायरटेक

19
ध्यान दें कि "मानक" रूबी मुहावरे के लिए 'self.status = ACTIVE जब तक self.status' 'self.status' = = ACTIVE 'है
माइक वुडहाउस

1
जेफ पेरिन का जवाब वर्तमान में स्वीकृत के रूप में चिह्नित की तुलना में बहुत बेहतर है। default_scope डिफ़ॉल्ट मान सेट करने के लिए एक अस्वीकार्य समाधान है, क्योंकि इसमें प्रश्नों के व्यवहार को बदलने का बहुत बड़ा प्रभाव है।
लॉरेंस


2
इस सवाल के लिए सभी upvotes को देखते हुए, मैं कहूंगा कि Ruby को
ActiveDecord के

जवाबों:


557

उपलब्ध तरीकों में से प्रत्येक के साथ कई समस्याएं हैं, लेकिन मेरा मानना ​​है कि after_initializeकॉलबैक को परिभाषित करना निम्नलिखित कारणों से जाने का तरीका है:

  1. default_scopeनए मॉडल के लिए मूल्यों को इनिशियलाइज़ करेगा, लेकिन फिर वह स्कोप बन जाएगा, जिस पर आप मॉडल पाते हैं। अगर आप सिर्फ कुछ नंबरों को 0 में इनिशियलाइज़ करना चाहते हैं तो यह वह नहीं है जो आप चाहते हैं।
  2. आपके प्रवास में चूक को परिभाषित करना भी समय का हिस्सा है ... जैसा कि पहले ही उल्लेख किया गया है कि जब आप सिर्फ Model.new कहते हैं तो यह काम नहीं करेगा ।
  3. ओवरराइडिंग initializeकाम कर सकती है, लेकिन कॉल करना न भूलें super!
  4. फ़्यूज़न जैसे प्लगइन का उपयोग करना थोड़ा हास्यास्पद है। यह रूबी है, क्या हमें वास्तव में कुछ डिफ़ॉल्ट मूल्यों को इनिशियलाइज़ करने के लिए एक प्लगइन की आवश्यकता है?
  5. ओवरराइडिंग after_initialize को रेल के रूप में चित्रित किया जाता है । 3. जब मैं after_initializeरेल 3.0.3 में ओवरराइड करता हूं तो मुझे कंसोल में निम्नलिखित चेतावनी मिलती है:

मूल्यांकन चेतावनी: आधार # after_initialize हटा दिया गया है, कृपया इसके बजाय Base.after_initialize: पद्धति का उपयोग करें। (/ यूजर्स / मी / मैप्प / ऐप / मॉडल / my_model से कॉल: 15)

इसलिए मैं कहूंगा after_initializeकि आपको कॉलबैक लिखना चाहिए , जिससे आप एसोसिएशनों पर डिफ़ॉल्ट दोषों के अलावा डिफ़ॉल्ट विशेषताएँ दे सकते हैं:

  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    

अब आपके पास अपने मॉडलों के आरंभीकरण को देखने के लिए सिर्फ एक जगह है। मैं इस विधि का उपयोग तब तक कर रहा हूं जब तक कि कोई बेहतर तरीके से नहीं आता।

चेतावनियां:

  1. बूलियन खेतों के लिए:

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

    अधिक विवरण के लिए इस उत्तर पर पॉल रसेल की टिप्पणी देखें

  2. यदि आप केवल एक मॉडल के लिए स्तंभों के सबसेट का चयन कर रहे हैं (जैसे, selectएक क्वेरी का उपयोग करके Person.select(:firstname, :lastname).all) तो आपको मिलेगा MissingAttributeErrorयदि आपकी initविधि एक स्तंभ तक पहुंचती है जो selectखंड में शामिल नहीं की गई है । आप इस तरह के मामले से बच सकते हैं:

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

    और एक बुलियन कॉलम के लिए ...

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

    यह भी ध्यान दें कि वाक्य रचना रेल 3.2 से पहले अलग है (नीचे क्लिफ डार्लिंग की टिप्पणी देखें)


7
यह निश्चित रूप से इसे प्राप्त करने का सबसे अच्छा तरीका प्रतीत होता है। जो वास्तव में अजीब और दुर्भाग्यपूर्ण है। निर्माण पर मॉडल विशेषता डिफॉल्ट्स की स्थापना के लिए एक समझदार पसंदीदा तरीका ऐसा लगता है जैसे कुछ चीजें पहले से ही निर्मित होनी चाहिए। केवल अन्य (विश्वसनीय) तरीके से, ओवरराइडिंग initialize, बस कुछ के लिए वास्तव में जटिल लगता है जो स्पष्ट और अच्छी तरह से परिभाषित होना चाहिए। मैंने यहां खोज करने से पहले दस्तावेज रेंगते हुए घंटों बिताए क्योंकि मुझे लगा कि यह कार्यक्षमता पहले से ही कहीं थी और मुझे इसके बारे में पता नहीं था।
सीनेशबोफ

106
इस पर एक ध्यान दें - यदि आपके पास एक बूलियन फ़ील्ड है जिसे आप डिफ़ॉल्ट करना चाहते हैं, तो ऐसा न करें self.bool_field ||= true, क्योंकि यह फ़ील्ड को सही करने के लिए बाध्य करेगा भले ही आप स्पष्ट रूप से इसे गलत तरीके से शुरू करते हों। इसके बजाय करो self.bool_field = true if self.bool_field.nil?
पॉल रसेल

2
बिंदु # 2 के बारे में, Model.new वास्तव में काम करता है (केवल मेरे लिए?) एक साथ माइग्रेशन में परिभाषित चूक के साथ, या अधिक सटीक रूप से तालिका स्तंभों के लिए डिफ़ॉल्ट मानों के साथ। लेकिन मुझे पता है कि after_initialize कॉलबैक पर आधारित जेफ का तरीका शायद सबसे अच्छा तरीका है। बस एक सवाल: क्या यह गंदे अभी तक बेकार वस्तुओं के साथ काम करता है? आपके उदाहरण में, क्या Person.new.number_was 0.0 लौटाएगा?
लॉरेंट फ़र्ज़ी

21
सक्रिय रिकॉर्ड के साथ विशिष्ट स्तंभों के चयन के साथ संयुक्त इस दृष्टिकोण का उपयोग करते समय सावधानी। इस स्थिति में केवल क्वेरी में निर्दिष्ट विशेषताएँ ऑब्जेक्ट में मिल जाएंगी और init कोड a को फेंक देगा MissingAttributeError। आप दिखाए गए अनुसार एक अतिरिक्त जांच जोड़ सकते हैं: self.number ||= 0.0 if self.has_attribute? :number बुलियन के लिए self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?:। यह रेल 3.2+ है - पहले के लिए, उपयोग self.attributes.has_key?, और आपको एक प्रतीक के बजाय एक स्ट्रिंग की आवश्यकता है।
क्लिफ डार्लिंग

6
संघों के साथ ऐसा करने से उन एसोसिएशनों को लुकअप पर लोड किया जाएगा। शुरू initializeके साथ return if !new_record?प्रदर्शन के मुद्दों से बचने के लिए।
काइल मेस्सी

68

रेल 5+

आप अपने मॉडल में विशेषता विधि का उपयोग कर सकते हैं , उदाहरण के लिए:

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

आप defaultपैरामीटर में एक लैम्ब्डा भी पास कर सकते हैं । उदाहरण:

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

3
आह, यह मणि मैं देख रहा था! डिफ़ॉल्ट एक खरीद भी सकते हैं, उदाहरण के लिए डिफ़ॉल्ट: -> {Time.current.to_date}
schpet

1
प्रकार को दूसरे तर्क के रूप में निर्दिष्ट करना सुनिश्चित करें, अन्यथा प्रकार होगा Valueऔर कोई टाइपकास्ट नहीं किया जाएगा।
null

मेरी ख़ुशी के लिए, यह store_accessor के साथ भी काम करता है, उदाहरण के लिए store_accessor :my_jsonb_column, :localeआपको दिए गए तरीके को परिभाषित कर सकता हैattribute :locale, :string, default: 'en'
Ryan Romanchuk

ओह यह शानदार है, मुझे एक फॉर्म में दिखाने के लिए डिफॉल्ट की जरूरत है और यह शानदार काम करता है। धन्यवाद लुकास।
पॉल वॉटसन

एक अभी भी इन पर सेट कर सकते हैं nil। अगर वे nilDB not null+ DB नहीं हो सकते हैं + github.com/sshaw/keep_defaults मेरे अनुभव से जाने का तरीका है
sshaw

47

हम डेटाबेस में डिफ़ॉल्ट मानों को माइग्रेशन के माध्यम से डालते हैं ( :defaultप्रत्येक स्तंभ परिभाषा पर विकल्प निर्दिष्ट करके ) और सक्रिय रिकॉर्ड को इन मानों का उपयोग प्रत्येक विशेषता के लिए डिफ़ॉल्ट सेट करने के लिए करते हैं।

IMHO, इस दृष्टिकोण को AR के सिद्धांतों के साथ संरेखित किया गया है: कॉन्फ़िगरेशन, DRY पर कन्वेंशन, तालिका परिभाषा मॉडल को चलाती है, न कि अन्य तरीके से।

ध्यान दें कि डिफॉल्ट अभी भी एप्लिकेशन (रूबी) कोड में हैं, हालांकि मॉडल में नहीं, लेकिन माइग्रेशन में हैं।


3
एक और समस्या है जब आप एक विदेशी कुंजी के लिए एक डिफ़ॉल्ट मान चाहते हैं। आप विदेशी कुंजी फ़ील्ड में ID मान को हार्ड-कोड नहीं कर सकते क्योंकि विभिन्न DB पर ID भिन्न हो सकती है।
शर्मीली

2
अभी तक एक और समस्या यह है कि इस तरह से आप गैर-स्थायी एक्सेसर्स (विशेषताएँ जो db कॉलम नहीं हैं) को प्रारंभ नहीं कर सकते हैं।
विक्टर ट्रॉन

2
एक और समस्या यह है कि आप सभी डिफ़ॉल्ट मानों को एक स्थान पर नहीं देख सकते हैं। वे विभिन्न प्रवासों के माध्यम से बिखरे हुए हो सकते हैं।
१६:११

8
डिकेलन, वहाँ db / schema.rb है
बेंजामिन एटकिन

6
मैं भविष्य के पाठकों के लिए उल्लेख करना चाहूंगा: कम से कम मैंने जो पढ़ा है, यह एआर के सिद्धांतों के विपरीत है। मॉडल के लिए तर्क मॉडल वर्गों के भीतर आराम करना चाहिए, और डेटाबेस जितना संभव हो उतना अज्ञानी होना चाहिए। मेरे लिए डिफ़ॉल्ट मान एक मॉडल के बारे में विशिष्ट तर्क का गठन करता है।
darethas

40

डेटाबेस स्कीमा में एक डिफ़ॉल्ट को परिभाषित करके कुछ सरल मामलों को संभाला जा सकता है, लेकिन यह कई पेचीदा मामलों की गणना नहीं करता है जिसमें अन्य मूल्यों और अन्य मॉडलों की कुंजी शामिल है। इन मामलों के लिए मैं यह करता हूं:

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 का उपयोग करने का निर्णय लिया है, लेकिन मैं नहीं चाहता कि इसे उन वस्तुओं पर लागू किया जाए जो केवल नए या बनाए गए हैं। मुझे लगता है कि यह लगभग चौंकाने वाला है कि एक after_new कॉलबैक इस स्पष्ट उपयोग के मामले के लिए प्रदान नहीं किया गया है, लेकिन मैंने यह पुष्टि करके किया है कि क्या ऑब्जेक्ट पहले से ही यह दर्शाता है कि यह नया नहीं है।

ब्रैड मरे के उत्तर को देखने के बाद यह स्थिति और भी साफ हो जाती है अगर इस स्थिति को कॉलबैक अनुरोध पर ले जाया जाता है:

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
यह वास्तव में एक महत्वपूर्ण बिंदु है। मुझे यह कल्पना करनी है कि अधिकांश मामलों में रिकॉर्ड पर डिफ़ॉल्ट सेट करना केवल एक नया रिकॉर्ड जारी करने से पहले किया जाना है, न कि लगातार रिकॉर्ड लोड करने पर।
रसेल सिल्वा

Thx दोस्त, तुमने मेरा दिन बचा लिया।
स्टेफ़नफ्रेंड्रीच

2
कैसे के बारे में :before_create?
फ्रैंकलिन यू

कैसे करता है: इससे पहले कि अलग-अलग नए हैंडल को बचाएं और कॉल सेव करें? मैं यह जांचना चाहूंगा कि वास्तव में इसे स्विच करने से पहले समझें।
यूसुफ लॉर्ड

17

After_initialize कॉलबैक पैटर्न में केवल निम्न कार्य करके सुधार किया जा सकता है

after_initialize :some_method_goes_here, :if => :new_record?

यदि आपके init कोड को संघों से निपटने की आवश्यकता है, तो इसका गैर-तुच्छ लाभ है, क्योंकि यदि आप संबंधित सहित बिना प्रारंभिक रिकॉर्ड पढ़ते हैं, तो निम्न कोड एक सूक्ष्म n + 1 को ट्रिगर करता है।

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

16

फ़्यूज़न लोगों के पास इसके लिए कुछ अच्छे प्लगइन हैं


ध्यान दें, यह प्लगइन :defaultस्कीमा माइग्रेशन में मूल्यों को 'बस काम' करने की अनुमति देता है Model.new
16

जेफ ने अपनी पोस्ट में जो कहा है, उसके विपरीत, मैं :default'सिर्फ काम' के लिए प्रवासन में मान प्राप्त कर सकता हूं Model.new। रेल में काम करने वाले सत्यापित 4.1.16।
मैग्ने

8

प्रस्तावित उत्तरों की तुलना में एक बेहतर / साफ-सुथरा संभावित तरीका, इस तरह से एक्सेसर को अधिलेखित करना है:

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

ActiveRecord में "ओवर राइटिंग डिफॉल्ट एक्सेसर्स" देखें :: बेस डॉक्यूमेंटेशन और स्वयं का उपयोग करने पर StackOverflow से अधिक


हैश से लौटे स्थिति अभी भी शून्य होगी attributes। रेल में परीक्षण किया 5.2.0।
स्पाइल

8

मणि का उपयोग करता हूंattribute-defaults

प्रलेखन से: चलाएं sudo gem install attribute-defaultsऔर require 'attribute_defaults'अपने ऐप में जोड़ें ।

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

इसी तरह के प्रश्न, लेकिन सभी में थोड़ा अलग संदर्भ है: - मैं रेल्स एक्टिवरकॉर्ड के मॉडल में विशेषताओं के लिए एक डिफ़ॉल्ट मान कैसे बनाऊं?

सर्वश्रेष्ठ उत्तर: आप जो चाहते हैं उस पर निर्भर करता है!

यदि आप चाहते हैं कि हर वस्तु मूल्य के साथ शुरू हो: उपयोग करेंafter_initialize :init

आप चाहते हैं कि new.htmlफॉर्म पेज खोलने पर एक डिफ़ॉल्ट मान हो? 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 

यदि आप चाहते हैं कि हर वस्तु के लिए एक मूल्य के उपयोगकर्ता इनपुट से गणना की है: उपयोगbefore_save :default_values आप उपयोगकर्ता दर्ज करना चाहते हैंXऔर उसके बादY = X+'foo'? उपयोग:

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

4

यह वही है जो निर्माणकर्ता हैं! मॉडल की initializeविधि को ओवरराइड करें ।

after_initializeविधि का प्रयोग करें ।


2
आम तौर पर आप सही होंगे लेकिन आपको कभी भी ActiveRecord मॉडल में इनिशियलाइज़ नहीं करना चाहिए क्योंकि इसे हमेशा नहीं कहा जा सकता है। आपको after_initializeइसके बजाय विधि का उपयोग करना चाहिए ।
ल्यूक रेडपैथ

केवल डिफ़ॉल्ट सेट करने के लिए default_scope का उपयोग करना CERTAINLY गलत है। after_initialize सही उत्तर है।
joaomilho

4

सुपर दोस्तों, मैंने निम्नलिखित काम किया:

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

एक जादू की तरह काम करता है!


3

यह लंबे समय से उत्तर दिया गया है, लेकिन मुझे अक्सर डिफ़ॉल्ट मानों की आवश्यकता होती है और उन्हें डेटाबेस में नहीं रखना पसंद करते हैं। मैं एक DefaultValuesचिंता पैदा करता हूं :

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

और फिर इसे मेरे मॉडल में उपयोग करें जैसे:

class Widget < ApplicationRecord
  include DefaultValues

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

3

मैंने यह भी देखा है कि लोग इसे अपने माइग्रेशन में रखते हैं, लेकिन मैं इसे मॉडल कोड में परिभाषित करता हूँ।

ActiveRecord मॉडल में फ़ील्ड के लिए डिफ़ॉल्ट मान सेट करने के लिए एक विहित तरीका है?

रेल 5 से पहले विहित रेल मार्ग, वास्तव में इसे माइग्रेशन में सेट करने के लिए था, और db/schema.rbजब भी कोई मॉडल के लिए डीबी द्वारा डिफ़ॉल्ट मान निर्धारित किए जा रहे हैं, यह देखना चाहते हैं।

@Jeff Perrin का उत्तर क्या है (जो थोड़ा पुराना है) के विपरीत Model.new, कुछ रेल जादू के कारण माइग्रेशन दृष्टिकोण डिफ़ॉल्ट का उपयोग करते समय भी लागू होगा । रेल में काम करने वाले सत्यापित 4.1.16।

सबसे सरल बात अक्सर सबसे अच्छी होती है। कम ज्ञान ऋण और कोडबेस में भ्रम के संभावित बिंदु। और यह 'सिर्फ काम करता है'।

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

या, एक नया निर्माण किए बिना स्तंभ परिवर्तन के लिए, फिर या तो करें:

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

या शायद इससे भी बेहतर:

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

कॉलम परिवर्तन विधियों में विकल्पों के लिए आधिकारिक RoR गाइड की जाँच करें।

null: falseअनुमति नहीं देता है डीबी में शून्य मानों, और, के रूप में एक अतिरिक्त लाभ है, यह भी तो अद्यतन करता है सब पहले से मौजूद DB रिकॉर्ड है कि पहले अशक्त थे और साथ ही इस क्षेत्र के लिए डिफ़ॉल्ट मान के साथ सेट है। यदि आप चाहें तो इस पैरामीटर को माइग्रेशन में बाहर कर सकते हैं, लेकिन मुझे यह बहुत आसान लगा!

रेल में रास्ता विहित 5+ है, जैसा कि @ लुकास केटन ने कहा:

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

1

After_initialize समाधान के साथ समस्या यह है कि आपको DB से बाहर दिखाई देने वाली हर एक वस्तु के लिए after_initialize जोड़ना होगा, भले ही आप इस विशेषता तक पहुंचें या नहीं। मैं एक आलसी-भारित दृष्टिकोण का सुझाव देता हूं।

एट्रिब्यूट मेथड्स (गेटर्स) खुद निश्चित तौर पर होते हैं, इसलिए आप उन्हें ओवरराइड कर सकते हैं और डिफॉल्ट प्रदान कर सकते हैं। कुछ इस तरह:

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

जब तक किसी ने बताया, आपको Foo.find_by_status ('सक्रिय') करने की आवश्यकता है। उस स्थिति में, मुझे लगता है कि आपको अपने डेटाबेस की कमी में डिफ़ॉल्ट रूप से सेट करने की आवश्यकता होगी, अगर DB इसका समर्थन करता है।


यह समाधान और प्रस्तावित विकल्प मेरे मामले में काम नहीं करते हैं: मेरे पास एक एसटीआई श्रेणी पदानुक्रम है जहां केवल एक वर्ग में वह विशेषता है, और डीबी क्वेरी स्थितियों में उपयोग किया जाने वाला संबंधित कॉलम।
cmoran92

1

जब मैं जटिल खोज करता हूं तो मैं त्रुटियां after_initializeदेता हूं ActiveModel::MissingAttributeError:

उदाहरण के लिए:

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

"खोज" .whereशर्तों के हैश में है

इसलिए मैंने इसे इस तरह इनिशियलाइज़ ओवरराइड करके खत्म किया:

def initialize
  super
  default_values
end

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

superकॉल यकीन है कि वस्तु से सही ढंग से आरंभ करने के लिए आवश्यक है ActiveRecord::Baseमेरी अनुकूलित कोड, यानी करने से पहले: default_values


मुझें यह पसंद है। मुझे def initialize(*); super; default_values; endरेल 5.2.0 में करने की आवश्यकता थी । साथ ही, डिफ़ॉल्ट मान उपलब्ध है, .attributesहैश में भी ।
स्पाइल

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

  before_save{ self.status ||= ACTIVE }
end

2
मम्म्म ... लगता है पहले तो हतप्रभ था, लेकिन थोड़ा सोचने के बाद, मैं कुछ समस्याओं को देखता हूं। सबसे पहले, सभी डिफ़ॉल्ट मान एक बिंदु में नहीं हैं, लेकिन कक्षा के माध्यम से बिखरे हुए हैं (उन्हें खोजने या उन्हें बदलने की कल्पना करें)। दूसरा और सबसे खराब, यो नहीं डाल सकते हैं, बाद में, एक शून्य मान (या एक झूठी भी!)।
परडोजा

आपको डिफ़ॉल्ट रूप में शून्य मान सेट करने की आवश्यकता क्यों होगी? आपको एआर के साथ बॉक्स से बाहर कुछ भी करने के बिना मिलता है। बूलियन कॉलम का उपयोग करते समय झूठे के लिए तब आप सही हैं, यह सबसे अच्छा तरीका नहीं है।
माइक ब्रिन

मैं दूसरों की कोडिंग की आदतों के लिए बात नहीं कर सकता, क्योंकि मेरे पास कोई मुद्दा नहीं है क्योंकि मैं अपने गेटर्स / क्लास क्लास के आसपास नहीं बिखरता। इसके अलावा, किसी भी आधुनिक टेक्स्ट एडिटर को एक विधि पर नेविगेट करना आसान बनाना चाहिए (टेक्स्टमेट में शिफ्ट-सीएमडी-टी)।
माइक ब्रिन

@ अपरदोजा - मैं इसे वापस लेता हूं, अब मैं देखता हूं कि यह नल के उपयोग से भी टूट जाता है। जरूरी नहीं कि अशक्त को डिफ़ॉल्ट के रूप में उपयोग किया जाए लेकिन यदि आप वास्तव में किसी बिंदु पर मान को बदलना चाहते हैं। अच्छा कैच @ अपरदोजा, धन्यवाद।
माइक ब्रिन नोव

मैं इस पद्धति का उपयोग करता हूं क्योंकि यह गतिशील रूप से उत्पन्न विशेषताओं के साथ अच्छी तरह से काम करता है।
डेला कैंपबेल

0

हालाँकि, डिफ़ॉल्ट मान सेट करने के लिए ज्यादातर मामलों में भ्रमित और अजीब बात है, आप इसका भी उपयोग कर सकते हैं :default_scope। की जाँच करें यहाँ squil की टिप्पणी


0

after_initialize पद्धति को हटा दिया गया है, इसके बजाय कॉलबैक का उपयोग करें।

after_initialize :defaults

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

हालाँकि, उपयोग करना : आपके माइग्रेशन में डिफ़ॉल्ट अभी भी सबसे साफ तरीका है।


4
रेल 3 में: after_initializeविधि है पदावनत नहीं । वास्तव में, मैक्रो-शैली की कॉलबैक आप आईएस के एक उदाहरण के रूप में देते हैं । विवरण: guide.rubyonrails.org/…
Zabba

0

मैंने पाया है कि सत्यापन विधि का उपयोग करने से चूक को नियंत्रित करने में बहुत अधिक नियंत्रण मिलता है। आप अपडेट के लिए चूक (या सत्यापन को विफल) भी सेट कर सकते हैं। यदि आप वास्तव में चाहते हैं, तो आप आवेषण बनाम अपडेट के लिए एक अलग डिफ़ॉल्ट मान भी सेट करते हैं। ध्यान दें कि डिफ़ॉल्ट #valid तक सेट नहीं किया जाएगा? कहा जाता है।

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 पद्धति को परिभाषित करने के बारे में, प्रदर्शन के मुद्दे हो सकते हैं क्योंकि after_initialize को प्रत्येक ऑब्जेक्ट द्वारा लौटाया भी जाता है: खोज: http://guides.rubyonrails.org/active_record_validations_allbacks.html#after_initialize-and-after_find


केवल सहेजने से पहले सत्यापन नहीं होता है? क्या होगा अगर आप बचत करने से पहले चूक दिखाना चाहते हैं?
nurettin

@nurettin यह एक अच्छा बिंदु है और मैं देख सकता हूँ कि आप ऐसा क्यों चाहते हैं कि कभी-कभी, लेकिन ओपी ने एक आवश्यकता के रूप में इसका उल्लेख नहीं किया। आपको अपने लिए तय करना होगा कि क्या आप हर उदाहरण पर चूक को सेट करने का ओवरहेड चाहते हैं, भले ही यह सहेजा नहीं गया हो। विकल्प यह है कि newकार्रवाई के पुन: उपयोग के लिए एक डमी वस्तु को इधर-उधर रखा जाए ।
केल्विन

0

यदि स्तंभ 'स्थिति' प्रकार का स्तंभ होता है, और आपका मॉडल राज्य मशीनों के उपयोग के लिए स्वयं को उधार देता है, तो ऐस्मा रत्न का उपयोग करने पर विचार करें , जिसके बाद आप बस कर सकते हैं

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

यह अभी भी सहेजे नहीं गए रिकॉर्ड्स के लिए मूल्य को इनिशियलाइज़ नहीं करता है, लेकिन यह आपके initया जो कुछ भी है उसके साथ रोल करने की तुलना में थोड़ा क्लीनर है , और आप ऐस के अन्य फ़ायदे जैसे कि स्कोप्स को अपने सभी स्टेटस के लिए पुनः प्राप्त करते हैं।



0

मैं दृढ़ता से "default_value_for" मणि का उपयोग करने का सुझाव देता हूं: https://github.com/FooBarWidget/default_value_for

कुछ ट्रिकी परिदृश्य हैं जिन्हें बहुत पहले इनिशियलाइज़ विधि की आवश्यकता होती है, जो कि मणि करता है।

उदाहरण:

आपका db डिफ़ॉल्ट NULL है, आपका मॉडल / रूबी-परिभाषित डिफ़ॉल्ट "कुछ स्ट्रिंग" है, लेकिन आप वास्तव में जो भी कारण के लिए शून्य करने के लिए मान सेट करना चाहते हैं :MyModel.new(my_attr: nil)

यहां अधिकांश समाधान शून्य पर मान सेट करने में विफल होंगे, और इसके बजाय इसे डिफ़ॉल्ट पर सेट करेंगे।

ठीक है, इसलिए ||=एप्रोच लेने के बजाय , आप स्विच ऑन my_attr_changed?...

लेकिन अब कल्पना करें कि आपका डीबी डिफ़ॉल्ट "कुछ स्ट्रिंग" है, आपका मॉडल / रूबी-परिभाषित डिफ़ॉल्ट "कुछ अन्य स्ट्रिंग" है, लेकिन एक निश्चित परिदृश्य के तहत, आप "कुछ स्ट्रिंग" (डीबी डिफ़ॉल्ट) के लिए मान सेट करना चाहते हैं :MyModel.new(my_attr: 'some_string')

यह गलतmy_attr_changed? होने के परिणामस्वरूप होगा क्योंकि मूल्य डीबी डिफ़ॉल्ट से मेल खाता है, जो बदले में आपके रूबी-परिभाषित डिफ़ॉल्ट कोड को आग देगा और मान को "कुछ अन्य स्ट्रिंग" पर सेट करेगा - फिर से, जो आप चाहते हैं वह नहीं।


उन कारणों से मुझे नहीं लगता कि यह ठीक से सिर्फ एक after_initialize हुक के साथ पूरा किया जा सकता है।

फिर से, मुझे लगता है कि "default_value_for" रत्न सही दृष्टिकोण ले रहा है: https://github.com/FooBarWidget/default_value_for


0

यहाँ एक समाधान मैंने उपयोग किया है कि मैं थोड़ा हैरान था अभी तक जोड़ा नहीं गया है।

इसके दो भाग हैं। पहला भाग वास्तविक माइग्रेशन में डिफ़ॉल्ट सेट कर रहा है, और दूसरा भाग यह सुनिश्चित करने वाले मॉडल में एक सत्यापन जोड़ रहा है कि उपस्थिति सही है।

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

तो आप यहां देखेंगे कि डिफ़ॉल्ट पहले से सेट है। अब सत्यापन में आप यह सुनिश्चित करना चाहते हैं कि स्ट्रिंग के लिए हमेशा एक मूल्य है, इसलिए बस करें

 validates :new_team_signature, presence: true

यह क्या करेगा आपके लिए डिफ़ॉल्ट मान सेट है। (मेरे लिए मेरे पास "टीम में आपका स्वागत है"), और फिर यह एक कदम आगे बढ़ेगा यह सुनिश्चित करेगा कि उस वस्तु पर हमेशा एक मूल्य मौजूद हो।

उम्मीद है की वो मदद करदे!


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

3 में default_scope का उपयोग करें

आपी डॉक्टर

ActiveRecord डेटाबेस (स्कीमा) में परिभाषित डिफ़ॉल्ट और अनुप्रयोग (मॉडल) में किए गए डिफ़ॉल्ट के बीच अंतर को अस्पष्ट करता है। आरंभीकरण के दौरान, यह डेटाबेस स्कीमा को पार्स करता है और वहां निर्दिष्ट किसी भी डिफ़ॉल्ट मान को नोट करता है। बाद में, ऑब्जेक्ट बनाते समय, यह डेटाबेस को छुए बिना उन स्कीमा-निर्दिष्ट डिफ़ॉल्ट मानों को असाइन करता है।

विचार-विमर्श


यदि आप मेटा_ वे का उपयोग करते हैं, तो default_scope बग के कारण नए AR ऑब्जेक्ट में डिफॉल्ट को असाइन करने के लिए काम नहीं कर सकता है।
विक्टर ट्रोन

यह मेटा-वे मुद्दा अब ठीक कर दिया गया है [ metautonomous.lIIIouseapp.com/projects/53011/ticket/…
विक्टर ट्रॉन

3
उपयोग नहीं करते default_scope। यह आपके सभी प्रश्नों को आपके द्वारा निर्धारित फ़ील्ड में इस स्थिति को जोड़ देगा। यह लगभग आप चाहते हैं कि क्या है।
ब्रैड

@ ब्रैड, मजाकिया उल्लेख, मैं पूरी तरह से सहमत हूं, यह बुराई है :)। मेरी टिप्पणी stackoverflow.com/questions/10680845/… पर देखें
विक्टर ट्रॉन

-3

एपीआई डॉक्स से http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.htmlbefore_validation अपने मॉडल में विधि का उपयोग करें , यह आपको इस उदाहरण में उदाहरण (फिर से कोड लिया गया) बनाने और अपडेट करने के लिए विशिष्ट प्रारंभिक बनाने के विकल्प देता है। एपी डॉक्स उदाहरण से) नंबर फ़ील्ड को क्रेडिट कार्ड के लिए आरंभीकृत किया जाता है। आप जो भी मान चाहें सेट करने के लिए इसे आसानी से अनुकूलित कर सकते हैं

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

हैरानी इस बात की है कि उनके यहाँ सुझाव नहीं दिया गया है


इससे पहले कि जब तक वस्तु को बनाए रखने के लिए तैयार नहीं हो जाता, तब तक चूक नहीं होगी। यदि प्रक्रिया को बनाए रखने से पहले चूक को पढ़ने की जरूरत है तो मान तैयार नहीं होंगे।
एमएमएल

आप कभी भी सत्यापन के दौरान डिफ़ॉल्ट मान सेट नहीं करते हैं। यह कोई हैक भी नहीं है। इनिशियलाइज़ेशन के दौरान करें
सचिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.