रेल जादू बनाते हैं या अपडेट करते हैं?


93

मेरे पास एक वर्ग है जिसे CachedObjectकुंजी द्वारा अनुक्रमित सामान्य क्रमबद्ध वस्तुओं को संग्रहीत किया जाता है। मैं चाहता हूं कि यह वर्ग ए लागू करेcreate_or_update विधि । यदि कोई वस्तु मिलती है तो वह उसे अपडेट कर देगा, अन्यथा यह एक नया निर्माण करेगा।

क्या रेल में ऐसा करने का कोई तरीका है या क्या मुझे अपना तरीका लिखना होगा?

जवाबों:


172

रेलें ६

रेल 6 ने एक upsertऔर upsert_allतरीके जोड़े जो इस कार्यक्षमता को वितरित करते हैं।

Model.upsert(column_name: value)

[upsert] यह किसी भी मॉडल को त्वरित नहीं करता है और न ही सक्रिय रिकॉर्ड कॉलबैक या सत्यापन को ट्रिगर करता है।

रेल 5, 4, और 3

नहीं यदि आप एक "upsert" की तलाश कर रहे हैं (जहाँ डेटाबेस एक अद्यतन या एक ही ऑपरेशन में एक सम्मिलित कथन निष्पादित करता है) प्रकार का बयान। बॉक्स से बाहर, रेल और ActiveRecord में ऐसी कोई सुविधा नहीं है। आप अपटाउन का उपयोग कर सकते हैं हालांकि मणि,।

अन्यथा, आप उपयोग कर सकते हैं: find_or_initialize_byयाfind_or_create_by , जो समान कार्यक्षमता प्रदान करते हैं, एक अतिरिक्त डेटाबेस हिट की कीमत पर, जो कि ज्यादातर मामलों में, शायद ही कोई मुद्दा है। इसलिए जब तक आपको गंभीर प्रदर्शन की चिंता नहीं है, मैं मणि का उपयोग नहीं करूंगा।

उदाहरण के लिए, यदि कोई उपयोगकर्ता "रोजर" नाम के साथ नहीं पाया जाता है, तो एक नया उपयोगकर्ता उदाहरण name"रोजर" के लिए सेट किया गया है।

user = User.where(name: "Roger").first_or_initialize
user.email = "email@example.com"
user.save

वैकल्पिक रूप से, आप उपयोग कर सकते हैं find_or_initialize_by

user = User.find_or_initialize_by(name: "Roger")

रेल में ३।

user = User.find_or_initialize_by_name("Roger")
user.email = "email@example.com"
user.save

आप एक ब्लॉक का उपयोग कर सकते हैं, लेकिन रिकॉर्ड नया होने पर ही ब्लॉक चलता है

User.where(name: "Roger").first_or_initialize do |user|
  # this won't run if a user with name "Roger" is found
  user.save 
end

User.find_or_initialize_by(name: "Roger") do |user|
  # this also won't run if a user with name "Roger" is found
  user.save
end

यदि आप रिकॉर्ड की दृढ़ता की परवाह किए बिना ब्लॉक का उपयोग करना चाहते हैं, tapतो परिणाम पर उपयोग करें :

User.where(name: "Roger").first_or_initialize.tap do |user|
  user.email = "email@example.com"
  user.save
end


1
स्रोत कोड जो दस्तावेज़ को इंगित करता है कि यह आपके द्वारा लगाए गए तरीके से काम नहीं करता है - ब्लॉक केवल एक नई विधि में पारित किया जाता है, यदि संबंधित रिकॉर्ड मौजूद नहीं है। प्रतीत होता है कि रेल में कोई "मुखर" जादू नहीं है - आपको इसे दो रूबी बयानों में अलग करना होगा, एक वस्तु चयन के लिए और एक विशेषता अद्यतन के लिए।
उसी सेपर

@sameers मुझे यकीन नहीं है कि मैं समझ सकता हूं कि आपका क्या मतलब है। आपको क्या लगता है कि मैं किस पर भरोसा कर रहा हूं?
मोहम्मद

1
ओह ... मैं देख रहा हूं कि अब आपका क्या मतलब है - यह दोनों रूपों, find_or_initialize_byऔर find_or_create_byएक ब्लॉक को स्वीकार करते हैं। मुझे लगा कि आपका मतलब है कि रिकॉर्ड मौजूद है या नहीं, एक ब्लॉक को रिकॉर्ड ऑब्जेक्ट के साथ एक तर्क के रूप में पारित किया जाएगा, ताकि अपडेट किया जा सके।
वही

3
यह थोड़ा भ्रामक है, जरूरी नहीं कि उत्तर, लेकिन एपीआई। किसी से अपेक्षा की जाएगी कि ब्लॉक की परवाह किए बिना पारित किया जाएगा, और इस प्रकार तदनुसार बनाया / अद्यतन किया जा सकता है। इसके बजाय, हमें इसे अलग-अलग बयानों में तोड़ना होगा। बू। <3 रेल हालांकि :)
Volte

32

रेल 4 में आप एक विशिष्ट मॉडल जोड़ सकते हैं:

def self.update_or_create(attributes)
  assign_or_new(attributes).save
end

def self.assign_or_new(attributes)
  obj = first || new
  obj.assign_attributes(attributes)
  obj
end

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

User.where(email: "a@b.com").update_or_create(name: "Mr A Bbb")

या यदि आप इन विधियों को इनिशियलाइज़र में रखे सभी मॉडलों में जोड़ना चाहते हैं:

module ActiveRecordExtras
  module Relation
    extend ActiveSupport::Concern

    module ClassMethods
      def update_or_create(attributes)
        assign_or_new(attributes).save
      end

      def update_or_create!(attributes)
        assign_or_new(attributes).save!
      end

      def assign_or_new(attributes)
        obj = first || new
        obj.assign_attributes(attributes)
        obj
      end
    end
  end
end

ActiveRecord::Base.send :include, ActiveRecordExtras::Relation

1
assign_or_newतालिका में पहली पंक्ति वापस नहीं आएगी यदि वह मौजूद है और फिर वह पंक्ति अपडेट हो जाएगी? ऐसा लगता है कि वह मेरे लिए कर रहा है।
स्टीवन क्लिन

User.where(email: "a@b.com").firstनहीं मिलने पर वापस कर देंगे। सुनिश्चित करें कि आपके पास एक whereगुंजाइश है
montrealmike

बस एक नोट कहना है कि updated_atक्योंकि छुआ नहीं किया जाएगा assign_attributesहै इस्तेमाल किया
lshepstone

यह नहीं है कि आप assing_or_new का उपयोग कर रहे हैं, लेकिन अगर आप save_or_create का उपयोग सहेजने के कारण करेंगे
montrealmike

13

इसे अपने मॉडल में जोड़ें:

def self.update_or_create_by(args, attributes)
  obj = self.find_or_create_by(args)
  obj.update(attributes)
  return obj
end

इसके साथ, आप कर सकते हैं:

User.update_or_create_by({name: 'Joe'}, attributes)

दूसरा हिस्सा मैं काम नहीं करूंगा। अद्यतन करने के लिए रिकॉर्ड की आईडी के बिना वर्ग स्तर से एक भी रिकॉर्ड अपडेट नहीं कर सकते।
एरोमर

1
obj = self.find_or_create_by (args); obj.update (विशेषताएँ); वापसी obj; काम करेगा।
वीरेश yh

12

जादू आप के लिए में जोड़ दिया गया है की तलाश में गया है Rails 6 अब आप कर सकते हैं Upsert (अद्यतन या सम्मिलित)। एकल रिकॉर्ड उपयोग के लिए:

Model.upsert(column_name: value)

कई रिकॉर्ड के लिए upsert_all का उपयोग करें :

Model.upsert_all(column_name: value, unique_by: :column_name)

नोट :

  • दोनों विधियाँ सक्रिय रिकॉर्ड कॉलबैक या सत्यापन को ट्रिगर नहीं करती हैं
  • unique_by => केवल PostgreSQL और SQLite

1

पुराना सवाल लेकिन पूर्णता के लिए मेरे समाधान को रिंग में फेंक देना। मुझे इसकी ज़रूरत तब पड़ी जब मुझे एक विशिष्ट खोज की आवश्यकता थी, लेकिन अगर यह मौजूद नहीं है, तो एक अलग बनाएं।

def self.find_by_or_create_with(args, attributes) # READ CAREFULLY! args for finding, attributes for creating!
        obj = self.find_or_initialize_by(args)
        return obj if obj.persisted?
        return obj if obj.update_attributes(attributes) 
end

0

आप इसे इस तरह एक बयान में कर सकते हैं:

CachedObject.where(key: "the given key").first_or_create! do |cached|
   cached.attribute1 = 'attribute value'
   cached.attribute2 = 'attribute value'
end

9
यह काम नहीं करेगा, क्योंकि यह केवल मूल रिकॉर्ड लौटाएगा यदि एक पाया जाता है। ओपी एक समाधान के लिए पूछता है जो हमेशा मूल्य बदलता है, भले ही एक रिकॉर्ड पाया जाए।
जीनमेर्ट्ज़

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