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


108

मैं रेल में वस्तुओं के लिए डिफ़ॉल्ट मान सेट करने का सबसे अच्छा तरीका खोजने की कोशिश कर रहा हूं।

जो सबसे अच्छा मैं सोच सकता हूं वह यह है कि इसमें डिफ़ॉल्ट मान सेट करना है new नियंत्रक में विधि में ।

क्या किसी के पास कोई इनपुट है अगर यह स्वीकार्य है या यदि ऐसा करने का कोई बेहतर तरीका है?


1
ये वस्तुएं क्या हैं; इनका सेवन / उपयोग कैसे किया जाता है? क्या उनका उपयोग विचारों को प्रस्तुत करते समय या नियंत्रक तर्क के लिए किया जाता है?
गिशु जू

3
यदि आप एक ActiveRecord ऑब्जेक्ट के बारे में बात कर रहे हैं, तो मुझे आपको बताना होगा कि 'डिफ़ॉल्ट मानों' की समस्या का कोई समाधान नहीं है। केवल पागल हैक्स, और रेल लेखकों को लगता नहीं है कि सुविधा इसके लायक है (कमाल है केवल रेल समुदाय है ..)
मौरिसियो

चूंकि स्वीकृत और अधिकांश उत्तर ActiveRecords पर ध्यान केंद्रित करते हैं, इसलिए हम मानते हैं कि मूल प्रश्न AcitveRecords के बारे में था। इसलिए stackoverflow.com/questions/328525/…
Ciro Santilli 郝海东 冠状 over over

जवाबों:


98

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

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 आपकी तालिका और स्तंभ गुणों को स्वतः खोजता है, इसलिए यह किसी भी मॉडल में किसी भी मानक रेल एप्लिकेशन का उपयोग करके उसी डिफ़ॉल्ट को सेट करने का कारण होगा।

हालाँकि, यदि आप केवल विशिष्ट मामलों में निर्धारित डिफ़ॉल्ट मान चाहते हैं - कहते हैं, यह एक विरासत में मिला मॉडल है जो कुछ अन्य लोगों के साथ एक तालिका साझा करता है - तो मॉडल ऑब्जेक्ट के निर्माण के समय एक और सुरुचिपूर्ण तरीका यह सीधे आपके रेल कोड में होता है:

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

फिर, जब आप एक करते हैं GenericPerson.new(), तो यह हमेशा "डो" विशेषता को Person.new()चकमा देगा जब तक कि आप इसे कुछ और के साथ ओवरराइड नहीं करते।


2
कोशिश करो। यह .newक्लास पद्धति के साथ बुलाए गए नए मॉडल ऑब्जेक्ट पर काम करेगा । ActiveRecord के सीधे कॉल करने के बारे में ब्लॉग पोस्ट की चर्चा .allocateडेटाबेस से मौजूदा डेटा के साथ लोड की गई मॉडल ऑब्जेक्ट्स के बारे में थी। ( और यह उस तरह से काम करने के लिए ActiveRecord के लिए एक भयानक विचार है, IMO। लेकिन इस बिंदु के पास है।)
SFEley

2
यदि आप मूल पोस्टर के प्रश्न को फिर से पढ़ने के लिए पीछे हटते हैं, तो निकिता, और फिर क्रम में मेरी टिप्पणी, यह आपके लिए अधिक समझ में आ सकता है। यदि नहीं ... तो, इस सवाल का जवाब दिया गया है। आपका दिन शुभ हो।
SFEley

8
डाउन माइग्रेशन के लिए बेहतर: change_column_default :people, :last_name, nil stackoverflow.com/a/1746246/483520
नोलन एमी

9
कृपया ध्यान दें कि नए रेल संस्करणों के लिए, आपको डिफ़ॉल्ट पैरामीटर से पहले एक अतिरिक्त प्रकार के पैरामीटर की आवश्यकता होती है। इसके लिए खोजें: इस पृष्ठ पर टाइप करें।
बेन व्हीलर

3
@JoelBrewer मैं आज इसमें भाग गया - रेल 4 के लिए, आपको कॉलम प्रकार भी निर्दिष्ट करने की आवश्यकता है:change_column :people, :last_name, :string, default: "Doe"
GoBusto

56

SFEley के उत्तर के आधार पर, नए रेल संस्करणों के लिए एक अद्यतन / निश्चित एक है:

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

2
खबरदार यह रेल ४.२.x पर काम नहीं करता (बाद के संस्करणों पर परीक्षण नहीं किया गया)। जैसा change_columnकि काफी "संरचित" हो सकता है, रिवर्स ऑपरेशन को घटाया नहीं जा सकता है। अगर आपको यकीन नहीं है, तो बस चलाकर db:migrateऔर db:rollbackसही से परीक्षण करें । एक ही परिणाम के रूप में स्वीकार किए जाते हैं लेकिन कम से कम यह माना जाता है!
gfd

1
यह मेरे लिए सही उत्तर है .. यह रेल 5.1 के साथ काम करता है, लेकिन आपको @GoBusto से ऊपर वर्णित upऔर downपसंद करना होगा
Fabrizio Bertoglio

22

सबसे पहले आप ओवरलोड नहीं कर सकते initialize(*args)क्योंकि यह सभी मामलों में नहीं कहा जाता है।

आपका सबसे अच्छा विकल्प है कि आप अपने प्रवासन में अपनी चूक डालें:

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

दूसरा सबसे अच्छा है कि अपने मॉडल में डिफॉल्ट को जगह दें लेकिन यह केवल उन विशेषताओं के साथ काम करेगा जो शुरू में शून्य हैं। आपको परेशानी हो सकती है जैसा मैंने booleanकॉलम के साथ किया था :

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

आप की जरूरत है new_record?ताकि चूक डेटाबेस से लोड मूल्यों को ओवरराइड न करें।

आपको ||=आरंभिक विधि में पारित मापदंडों को ओवरराइड करने से रोकने की आवश्यकता है।


12
छोटे नोट - आप दो काम करना चाहते हैं: .... 1) after_initialize आपके तरीके को कॉल न करें। आप चाहते हैं after_initiation :your_method_name.... 2 का उपयोग करेंself.max_users ||= 10
जेसी वल्गामोट 21

5
बूलियन्स के लिए, बस ऐसा करें: Prop = true if prop.nil?
फ्रांसिस पॉटर

2
या के after_initialize doबजायdef after_initialize
fotanus 17

self.max_users सुरक्षित होने के लिए।
केन रतनचाई एस।

15

आप change_column_defaultअपने प्रवास में भी कोशिश कर सकते हैं (रेल 3.2.8 में परीक्षण किया गया):

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 रेल एपीआई डॉक्स


1
स्वीकृत उत्तर अधिक पूर्ण है लेकिन मुझे यह साफ और पसंद आया है ... धन्यवाद!
gfd

7

यदि आप ActiveRecord ऑब्जेक्ट्स का उल्लेख कर रहे हैं, तो आपके पास ऐसा करने के दो तरीके (अधिक से अधिक) हैं:

1. DB में एक: डिफ़ॉल्ट पैरामीटर का उपयोग करें

ईजी

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

अधिक जानकारी यहाँ: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. एक कॉलबैक का उपयोग करें

ईजी before_validation_on_create

अधिक जानकारी यहाँ: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147


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

बूलियन 1 या 0 पर डिफ़ॉल्ट नहीं हो सकते हैं - उन्हें सही या गलत पर सेट किया जाना चाहिए (सिलास का जवाब देखें)।
जामुन होल्मग्रेन

@JamonHolmgren टिप्पणी के लिए धन्यवाद, मैंने उत्तर को सही किया :)
व्लाद ज़्लोटीनू

कोई बात नहीं @VladZloteanu
Jamon Holmgren

7

रूबी ऑन रेल्स v3.2.8 में, after_initializeActiveRecord कॉलबैक का उपयोग करके , आप अपने मॉडल में एक विधि को कॉल कर सकते हैं जो एक नए ऑब्जेक्ट के लिए डिफ़ॉल्ट मान असाइन करेगा।

after_initialize कॉलबैक को प्रत्येक ऑब्जेक्ट के लिए ट्रिगर किया जाता है जो एक खोजक द्वारा पाया और त्वरित किया जाता है, साथ ही after_initialize को नई वस्तुओं के तुरंत बाद ट्रिगर किया जाता है ( ActiveRecord Callbacks देखें )।

तो, IMO यह कुछ इस तरह दिखना चाहिए:

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इस उदाहरण के लिए जब तक कि attribute_whose_presence_has_been_validatedपहले से सेव / अपडेट पर उदाहरण न हो । default_valueतो प्रपत्र का उपयोग कर प्रस्तुत करने के लिए अपने दृश्य के साथ संयोजन के रूप में इस्तेमाल किया जाएगा default_valueके लिए barविशेषता।

सबसे अच्छा यह hacky है ...

EDIT - 'new_record?' का उपयोग करें यह जांचने के लिए कि क्या नए कॉल से तत्काल

किसी विशेषता मान की जाँच करने के बजाय, new_record?अंतर्निहित पद्धति का उपयोग रेल के साथ करें। तो, उपरोक्त उदाहरण को इस तरह दिखना चाहिए:

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

यह बहुत क्लीनर है। आह, रेल का जादू - यह मुझसे ज्यादा चालाक है।


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

1
आप after_initializeयहां कॉलबैक की सलाह क्यों देते हैं? before_createअतिरिक्त शर्तों की जांच के बिना कॉलबैक पर रेल प्रलेखन में डिफ़ॉल्ट मूल्य निर्धारित करने का उदाहरण है
Dfr

@ डीआरई - मुझे याद आया होगा कि, क्या आप मुझे समीक्षा के लिए एक लिंक दे सकते हैं और मैं उत्तर को अपडेट कर दूंगा ...
14

हाँ, यहाँ api.rubyonrails.org/classes/ActiveRecord/Callbacks.html पहला उदाहरण वर्गSubscription
डीएफआर

5
@ डीआरई - कॉलबैक के after_initializeबजाय हम जिस कारण का उपयोग कर रहे हैं , वह यह है कि क्या हम उपयोगकर्ता केbefore_create लिए एक डिफ़ॉल्ट मान सेट करना चाहते हैं (एक दृश्य में उपयोग के लिए) जब वे नई वस्तुओं का निर्माण कर रहे हैं। कॉलबैक कहा जाता है के बाद उपयोगकर्ता एक नई वस्तु की सेवा कर दिया गया है, उनके इनपुट प्रदान की है, और नियंत्रक करने के लिए निर्माण के लिए वस्तु प्रस्तुत की। नियंत्रक तब किसी कॉलबैक के लिए जाँच करता है । यह प्रति-सहज लगता है, लेकिन यह एक नामकरण वाली बात है - कार्रवाई को संदर्भित करता है । किसी नए ऑब्जेक्ट को इंस्टेंट करना ऑब्जेक्ट नहीं है। before_createbefore_createbefore_createcreatecreate
एरियल

5

कम से कम 3.2.6 रेल में बूलियन फ़ील्ड के लिए, यह आपके माइग्रेशन में काम करेगा।

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

एक लाना 1या 0एक डिफ़ॉल्ट के लिए यहाँ काम नहीं करेगा, क्योंकि यह एक बूलियन क्षेत्र है। यह एक trueया falseमान होना चाहिए ।


4

यदि आप एक मॉडल के साथ काम कर रहे हैं, तो आप रेलिंग एपीआई का उपयोग रेल में 5+ http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html_method-i-attribute का उपयोग कर सकते हैं

बस एक उचित कॉलम नाम के साथ एक माइग्रेशन जोड़ें और फिर मॉडल में इसे सेट करें:

class StoreListing < ActiveRecord::Base
  attribute :country, :string, default: 'PT'
end

2

माइग्रेशन और उपयोग उत्पन्न करें change_column_default, संक्षिप्त और प्रतिवर्ती है:

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

1

यदि आप किसी डेटाबेस समर्थित मॉडल की कुछ विशेषताओं के लिए डिफॉल्ट सेट कर रहे हैं, तो मैं sql डिफ़ॉल्ट कॉलम मानों का उपयोग करने पर विचार करूंगा - क्या आप स्पष्ट कर सकते हैं कि आप किस प्रकार के डिफॉल्ट्स का उपयोग कर रहे हैं?

इसे संभालने के लिए कई दृष्टिकोण हैं, यह प्लगइन एक दिलचस्प विकल्प की तरह दिखता है।


1
मुझे लगता है कि आपको चूक और बाधाओं से निपटने के लिए डेटाबेस पर निर्भर नहीं होना चाहिए, यह सब सामान को मॉडल परत में हल किया जाना चाहिए। वह प्लगइन केवल ActiveRecord मॉडल के लिए काम करता है, यह वस्तुओं के लिए डिफ़ॉल्ट सेट करने का सामान्य तरीका नहीं है।
लुकास स्टैस्कल

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

1

नए / आरंभ को ओवरराइड करने का सुझाव शायद अधूरा है। ActiveRecord ऑब्जेक्ट्स के लिए रेल (अक्सर) कॉल आबंटित होगी, और कॉल करने के लिए कॉल करने से कॉल को प्रारंभ नहीं किया जा सकेगा।

यदि आप ActiveRecord ऑब्जेक्ट्स के बारे में बात कर रहे हैं, तो after_initialize ओवरराइडिंग पर एक नज़र डालें।

ये ब्लॉग पोस्ट (मेरी नहीं) उपयोगी हैं:

डिफ़ॉल्ट मान डिफ़ॉल्ट निर्माता को नहीं बुलाया जाता है

[संपादित करें: SFEley बताती है कि रेल वास्तव में डेटाबेस में डिफ़ॉल्ट को देखती है जब यह स्मृति में एक नई वस्तु को तत्काल बदल देती है - मुझे इसका एहसास नहीं हुआ था।]


1

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

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

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

क्योंकि after_initialize कॉलबैक को तर्कों से विशेषता सेट करने के बाद कहा जाता है, यह जानने का कोई तरीका नहीं था कि क्या विशेषता nil है क्योंकि इसे कभी सेट नहीं किया गया था या क्योंकि यह जानबूझकर शून्य के रूप में सेट किया गया था। इसलिए मुझे थोड़ा अंदर घुसना पड़ा और इस सरल समाधान के साथ आया।

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

मेरे लिए महान काम करता है। (रेल 3.2.x)


1

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

def status
  self['name_of_var'] || 'desired_default_value'
end

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


0

मैं यहाँ एक समान सवाल का जवाब दिया .. यह करने के लिए एक साफ तरीका रेल attr_accessor_with_default का उपयोग कर रहा है

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

अपडेट करें

attr_accessor_with_default Rails 3.2 में पदावनत किया गया है। आप पवित्र सैरी के बजाय ऐसा कर सकते हैं

class SOF
  attr_writer :is_awesome

  def is_awesome
    @is_awesome ||= true
  end
end

sof = SOF.new
sof.is_awesome

#=> true

attr_accessor_with_defaultरेल के रूप में पदावनत किया जाता है> 3.1.0
फ़्लायनफ़िश

आपके उदाहरण में, is_awesome हमेशा सही रहेगा जब भी @is_awesome == गलत हो।
रिची

0

यदि आप ActiveRecord ऑब्जेक्ट्स के बारे में बात कर रहे हैं, तो मैं 'विशेषता-डिफॉल्ट्स' मणि का उपयोग करता हूं।

प्रलेखन और डाउनलोड: https://github.com/bsm/attribute-defaults



-3

आप ActiveRecord मॉडल के लिए निर्माता को ओवरराइड कर सकते हैं।

ऐशे ही:

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
कोर रेल की कार्यक्षमता को बदलने के लिए यह एक भयानक जगह है। अन्य चीजों को तोड़ने के लिए अन्य बहुत कम स्थिर हैं, अन्य उत्तर में उल्लेख करने के तरीके। बंदर पैचिंग केवल उन चीजों के लिए एक अंतिम उपाय होना चाहिए जो संभवतः किसी अन्य तरीके से नहीं किया जा सकता है।
विल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.