ActiveRecord में create पर overriding id


104

क्या किसी मॉडल की आईडी वैल्यू को ओवरराइड करने का कोई तरीका है? कुछ इस तरह:

Post.create(:id => 10, :title => 'Test')

आदर्श होगा, लेकिन स्पष्ट रूप से काम नहीं करेगा।


1
इनमें से कई उत्तर रुक-रुक कर 4 के साथ विफल होंगे और कहेंगे कि वे काम कर रहे हैं। एक स्पष्टीकरण के लिए मेरा जवाब देखें ।
रिक स्मिथ

जवाबों:


116

आईडी सिर्फ attr_protected है, यही वजह है कि आप इसे सेट करने के लिए बड़े पैमाने पर असाइनमेंट का उपयोग नहीं कर सकते हैं। हालाँकि, इसे मैन्युअल रूप से सेट करते समय, यह सिर्फ काम करता है:

o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888

मुझे यकीन नहीं है कि मूल प्रेरणा क्या थी, लेकिन मैं यह तब करता हूं जब ActiveHash मॉडल को ActiveRecord में परिवर्तित कर रहा हूं। ActiveHash आपको ActiveRecord में समान ही__ शब्दार्थ का उपयोग करने की अनुमति देता है, लेकिन माइग्रेशन होने और तालिका बनाने के बजाय, और हर कॉल पर डेटाबेस के ओवरहेड को लाइक करने के बाद, आप बस अपना डेटा yml फ़ाइलों में संग्रहीत करते हैं। डेटाबेस में विदेशी कुंजियाँ yml में इन-मेमोरी आईडी का संदर्भ देती हैं।

ActiveHash पिकलिस्ट्स और छोटे तालिकाओं के लिए बहुत अच्छा है जो अक्सर बदलते रहते हैं और केवल डेवलपर्स द्वारा बदलते हैं। इसलिए जब ActiveHash से ActiveRecord में जा रहे हैं, तो बस सभी विदेशी कुंजी संदर्भों को समान रखना आसान है।


@jkndrkn - मुझे नहीं पता कि आपका क्या मतलब है। मैं ActiveRecord::VERSION::STRING == "3.2.11"यहाँ (sqlite3 एडेप्टर के साथ) और मेरे लिए उपरोक्त काम करता है।
फेलिक्स राबे

शायद जो मैंने अनुभव किया वह केवल mysql ड्राइवर के साथ व्यक्त किया।
19 अक्टूबर को jkndrkn

@jkndrkn रेल्स के लिए MySQL ड्राइवर का उपयोग करता है 3.2.18 के लिए
lulalala

मेरे लिए काम किया। मेरी ज़िंदगी बचाई। रेलमार्ग पर प्रयुक्त 4.0.0 / पोस्टग्रेज 9.3.5
एलनवेनली

रेल 4 पर ऐसा करने के लिए मेरा उत्तर देखें ।
रिक स्मिथ

30

प्रयत्न

a_post = Post.new do |p| 
  p.id = 10
  p.title = 'Test'
  p.save
end

आपको वह देना चाहिए जो आप खोज रहे हैं।


2
यकीन नहीं तुम क्यों downvoted कर रहे, यह मेरे लिए अच्छा काम करता है
semanticart

यह एक्टिवरकॉर्ड 3.2.11 के रूप में काम करना जारी रखता है। जेफ डीन द्वारा 2 अक्टूबर 2009 को पोस्ट किया गया जवाब अब काम नहीं करता है।
jkndrkn

काम नहीं करता है, केवल p.save को कॉल करने के कारण काम करने लगता है, जो कि शायद गलत है। यदि आपने p.save चलाया है तो ActiveRecord :: RecordNotFound मिलेगा!
एलन

29

आप भी कुछ इस तरह का उपयोग कर सकते हैं:

Post.create({:id => 10, :title => 'Test'}, :without_protection => true)

यद्यपि डॉक्स में कहा गया है , यह बड़े पैमाने पर असाइनमेंट सुरक्षा को बायपास करेगा।


अच्छा लग रहा है, @Samuel Heaney, मैं इस कार्य को पूरी तरह से सत्यापित कर सकता हूं 3.2.13 के साथ।
mkralla11

मेरे लिए भी काम किया; एक अलग क्षेत्र शामिल करना आवश्यक नहीं था।
शालोट

2
काश, यह अब रेल 4 पर काम नहीं करता, तो उन्होंने विकल्प हटा दिया हैश
जॉर्ज संपोय

@ जोर्जसम्पायो - रेल 4 में आपको इसकी आवश्यकता नहीं होनी चाहिए क्योंकि स्ट्रॉन्गपैरैम्स के पक्ष में संरक्षित विशेषताओं को हटाया जा सकता है।
PinnyM

21

रेल के लिए 4:

Post.create(:title => 'Test').update_column(:id, 10)

अन्य रेल 4 उत्तर मेरे काम नहीं आए । उनमें से कई दिखाई दिएरेल कंसोल का उपयोग करते समय परिवर्तन करने के लिए , लेकिन जब मैंने MySQL डेटाबेस में मूल्यों की जांच की, तो वे अपरिवर्तित रहे। अन्य उत्तर केवल कभी-कभी काम करते थे।

MySQL के लिए कम से कम, idऑटो इंक्रीमेंट आईडी नंबर से नीचे असाइन करना तब तक काम नहीं करता है जब तक आप उपयोग नहीं करते हैं update_column। उदाहरण के लिए,

p = Post.create(:title => 'Test')
p.id
=> 20 # 20 was the id the auto increment gave it

p2 = Post.create(:id => 40, :title => 'Test')
p2.id
=> 40 # 40 > the next auto increment id (21) so allow it

p3 = Post.create(:id => 10, :title => 'Test')
p3.id
=> 10 # Go check your database, it may say 41.
# Assigning an id to a number below the next auto generated id will not update the db

यदि आप createउपयोग करने के लिए बदलते हैं new+ saveतो भी आपको यह समस्या होगी। मैन्युअल रूप से idजैसे बदल रहा हैp.id = 10 भी इस समस्या पैदा करता है।

सामान्य तौर पर, मैं एक अतिरिक्त डेटाबेस क्वेरी की लागत के बावजूद भी इसे update_columnबदलने के लिए उपयोग करूंगा idक्योंकि यह हर समय काम करेगा। यह एक त्रुटि है जो आपके विकास के माहौल में दिखाई नहीं दे सकती है, लेकिन काम करते हुए यह कहकर चुपचाप अपने उत्पादन डेटाबेस को भ्रष्ट कर सकती है।


3
यह भी एकमात्र तरीका था जो मुझे कंसोल में 3.2.22 की रेल में काम करने के लिए मिला था। 'सेव' पर विविधताओं का उपयोग करने वाले उत्तरों का कोई प्रभाव नहीं पड़ा।
जोसेफ

2
रेल के लिए 4+ मैं उपयोग करता हूं:Post.new.update(id: 10, title: 'Test')
स्पेन्सर

6

वास्तव में, यह पता चला है कि निम्नलिखित कार्य करना:

p = Post.new(:id => 10, :title => 'Test')
p.save(false)

जबकि यह काम कर सकता है, यह सभी मान्यताओं को भी बंद कर देता है, जो कि पूछे गए उद्देश्य के अनुसार नहीं हो सकता है।
जॉर्डन मोनक्रोमोंट

यह वही है जो मुझे बीज डेटा के लिए आवश्यक था जहां आईडी की बात है। धन्यवाद।
जद।

मैं मूल रूप से अपवित्र था, यह सोचते हुए कि यह बीज डेटा के लिए काम करेगा, जैसा कि @JD ने बताया, लेकिन फिर मैंने इसे एक्टिवरकॉर्ड 3.2.13 के साथ आज़माया और मुझे अभी भी "संरक्षित विशेषताओं को असाइन नहीं किया जा सका" त्रुटि मिलती है। तो,
नीचा दिखाया गया

1
दुर्भाग्य से, यह रेल 4 में काम नहीं करता है, आपको NoMethodError: अपरिभाषित विधि `[] 'झूठी' के लिए: FalseClass
Jorge Sampayo

@ जोर्जसम्पायो यह अभी भी काम करता है यदि आप validate: falseबस के बजाय पास करते हैं false। हालाँकि, आप अभी भी संरक्षित विशेषता के मुद्दे पर चलते हैं - वहाँ एक अलग तरीका है जो मैंने अपने उत्तर में उल्लिखित किया है।
३० पर पिन्नी जूल

6

जैसा कि जेफ़ बताते हैं, आईडी ऐसा व्यवहार करता है मानो attr_protected है। इसे रोकने के लिए, आपको डिफ़ॉल्ट संरक्षित विशेषताओं की सूची को ओवरराइड करना होगा। कहीं भी ऐसा करने में सावधानी बरतें कि विशेषता जानकारी बाहर से आ सकती है। आईडी फ़ील्ड डिफ़ॉल्ट रूप से किसी कारण से सुरक्षित है।

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(ActiveRecord 2.3.5 के साथ परीक्षण किया गया)


6

हम विशेषताएँ_प्रकारित_बाह्य_ ओवरफ़ॉल्ट को ओवरराइड कर सकते हैं

class Example < ActiveRecord::Base
    def self.attributes_protected_by_default
        # default is ["id", "type"]
        ["type"]
    end
end

e = Example.new(:id => 10000)

5
Post.create!(:title => "Test") { |t| t.id = 10 }

यह मुझे उस तरह की चीज के रूप में नहीं मारता है, जिसे आप सामान्य रूप से करना चाहते हैं, लेकिन यह बहुत अच्छी तरह से काम करता है यदि आपको आईडी के एक निश्चित सेट के साथ तालिका को पॉप्युलेट करने की आवश्यकता है (उदाहरण के लिए जब एक रेक कार्य का उपयोग करके चूक बनाते हैं) और आप ऑटो-इन्क्रिमेंटिंग को ओवरराइड करना चाहते हैं (ताकि हर बार जब आप कार्य को चलाएं तो टेबल एक ही आईडी से आबाद हो):

post_types.each_with_index do |post_type|
  PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end

2

इस create_with_id फ़ंक्शन को अपने seeds.rb के शीर्ष पर रखें और फिर इसका उपयोग अपने ऑब्जेक्ट निर्माण के लिए करें जहाँ स्पष्ट आईडी वांछित हैं।

def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
    obj
end

और इसे इस तरह से उपयोग करें

create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})

के बजाय का उपयोग करने का

Foo.create({id:1,name:"My Foo",prop:"My other property"})


2

यह मामला एक ऐसा ही मुद्दा है जो idएक तरह की कस्टम तिथि के साथ आवश्यक था :

# in app/models/calendar_block_group.rb
class CalendarBlockGroup < ActiveRecord::Base
...
 before_validation :parse_id

 def parse_id
    self.id = self.date.strftime('%d%m%Y')
 end
...
end

और तब :

CalendarBlockGroup.create!(:date => Date.today)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">

कॉलबैक ठीक काम करता है।

शुभ लाभ!।


मुझे idयूनिक्स टाइम स्टैम्प पर आधारित बनाने की जरूरत थी । मैंने वह अंदर कर लिया है before_create। ठीक काम करता है।
WM

0

रेल 3 के लिए, इसे करने का सबसे सरल तरीका शोधन के newसाथ उपयोग करना है without_protection, और फिर save:

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save

बीज डेटा के लिए, यह सत्यापन को बायपास करने के लिए समझ में आता है जो आप इस तरह कर सकते हैं:

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)

हमने वास्तव में ActiveRecord :: सहायता के लिए एक सहायक विधि जोड़ी है, जो बीज फ़ाइलों को निष्पादित करने से तुरंत पहले घोषित किया जाता है:

class ActiveRecord::Base
  def self.seed_create(attributes)
    new(attributes, without_protection: true).save(validate: false)
  end
end

और अब:

Post.seed_create(:id => 10, :title => 'Test')

रेल 4 के लिए, आपको संरक्षित विशेषताओं के बजाय स्ट्रांगपराम का उपयोग करना चाहिए। यदि यह मामला है, तो आप किसी भी झंडे को पारित किए बिना बस असाइन करने और सहेजने में सक्षम होंगे new:

Post.new(id: 10, title: 'Test').save      # optionally pass `{validate: false}`

{}ऊपर शमूएल के जवाब (Rails3) के रूप में विशेषताओं को डाले बिना मेरे लिए काम नहीं करता है ।
क्रिस्टोफर Oezbek

@PinnyM योर रेल्स 4 का जवाब मेरे लिए काम नहीं करता है। idअभी भी 10. है
रिक स्मिथ

@RickSmith दिए गए उदाहरण में, id10 के रूप में पारित किया गया था - इसलिए यह वास्तव में यही होना चाहिए। यदि वह नहीं है जो आप उम्मीद कर रहे थे, तो क्या आप एक उदाहरण के साथ स्पष्ट कर सकते हैं?
पिनीएम

क्षमा करें, मेरा कहने का मतलब अभी भी 10 नहीं है । स्पष्टीकरण के लिए मेरा जवाब देखें।
रिक स्मिथ

@RickSmith - दिलचस्प, क्या यह समस्या MySQL के लिए अद्वितीय है? किसी भी मामले में, प्राथमिक कुंजी सीधे असाइन करने के लिए सामान्य उपयोग बीज डेटा के लिए है। यदि ऐसा है, तो आपको आम तौर पर उन मूल्यों में प्रवेश करने का प्रयास नहीं करना चाहिए जो कि ऑटोइन्क्रिमेंट थ्रेशोल्ड से नीचे हैं, या आपको कमांड के उस सेट के लिए ऑटोइन्क्रिमेंट को बंद करना चाहिए।
PinnyM

0

Postgresql 9.5.3 के साथ रेल्स 4.2.1 में, Post.create(:id => 10, :title => 'Test')जब तक आईडी = 10 के साथ एक पंक्ति नहीं है , तब तक काम करता है।


0

आप sql द्वारा आईडी डाल सकते हैं:

  arr = record_line.strip.split(",")
  sql = "insert into records(id, created_at, updated_at, count, type_id, cycle, date) values(#{arr[0]},#{arr[1]},#{arr[2]},#{arr[3]},#{arr[4]},#{arr[5]},#{arr[6]})"
  ActiveRecord::Base.connection.execute sql
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.