developer tip

Rails는 마법을 만들거나 업데이트합니까?

optionbox 2020. 9. 9. 07:55
반응형

Rails는 마법을 만들거나 업데이트합니까?


CachedObject키로 인덱싱 된 일반 직렬화 된 객체를 저장 하는 클래스 가 있습니다. 이 클래스가 create_or_update메서드 를 구현하기를 원합니다 . 개체가 발견되면 업데이트하고, 그렇지 않으면 새 개체를 만듭니다.

Rails에서이 작업을 수행 할 수있는 방법이 있습니까? 아니면 내 자신의 방법을 작성해야합니까?


"upsert"(데이터베이스가 동일한 작업에서 업데이트 또는 삽입 문을 실행하는) 유형의 문을 찾는 경우에는 해당되지 않습니다. 기본적으로 Rails 및 ActiveRecord에는 이러한 기능이 없습니다. 그러나 upsert gem을 사용할 수 있습니다 .

: 그렇지 않으면, 당신은 사용할 수 있습니다 find_or_initialize_by또는 find_or_create_by대부분의 경우에, 전혀 거의 문제가 없다, 추가 데이터베이스 히트의 비용이기는하지만, 비슷한 기능을 제공한다. 따라서 심각한 성능 문제가 없으면 gem을 사용하지 않을 것입니다.

예를 들어, "Roger"라는 이름을 가진 사용자가 없으면 새 사용자 인스턴스가 name"Roger"로 설정된 상태로 인스턴스화됩니다 .

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")

Rails 3.

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

블록을 사용할 수 있지만 블록은 레코드가 new 인 경우에만 실행됩니다 .

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

Rails 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

Add this to your model:

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

With that, you can:

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

The magic you have been looking for has been added in Rails 6 Now you can upsert(update or insert).

Model.upsert(column_name: value)

Note: For bulk update or create you can use upsert_all


You can do it in one statement like this:

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

Old question but throwing my solution into the ring for completeness. I needed this when I needed a specific find but a different create if it doesn't exist.

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

The sequel gem adds an update_or_create method which seems to do what you're looking for.

참고URL : https://stackoverflow.com/questions/18747062/rails-create-or-update-magic

반응형