복붙노트

[RUBY-ON-RAILS] accepts_nested_attributes_for find_or_create와?

RUBY-ON-RAILS

accepts_nested_attributes_for find_or_create와?

나는 큰 성공과 방법 accepts_nested_attributes_for '레일을 사용하고 있지만 내가 어떻게 기록이 이미 존재하는 경우는 새 레코드를 생성 할 수 있을까요?

예로서 :

말 나는 세 가지 모델, 팀, 회원, 그리고 플레이어를 가지고,과 구성원을 통해 각 팀 has_many 플레이어, 선수들이 많은 팀에 속할 수 있습니다했습니다. 팀 모델은 플레이어 중첩 된 속성을 허용하지만 수도가 결합 된 팀 + 플레이어를 통해 제출 된 각 선수가 (들) 형태는 새로운 선수 레코드로 생성된다는 것을 의미합니다.

어떻게 이미 같은 이름을 가진 플레이어가없는 경우 난 단지 새로운 플레이어가이 방법을 기록 만들려면 일을 가야 하는가? 같은 이름을 가진 선수가 있다면, 새로운 선수의 기록은 생성되지해야하지만, 대신에 올바른 플레이어는 발견되어야하고 새로운 팀 기록과 관련.

해결법

  1. ==============================

    1.당신이 자동 저장 협회의 훅을 정의 할 때, 일반적인 코드 경로는 생략하고 당신의 방법 대신이라고합니다. 따라서, 당신은이 작업을 수행 할 수 있습니다 :

    당신이 자동 저장 협회의 훅을 정의 할 때, 일반적인 코드 경로는 생략하고 당신의 방법 대신이라고합니다. 따라서, 당신은이 작업을 수행 할 수 있습니다 :

    class Post < ActiveRecord::Base
      belongs_to :author, :autosave => true
      accepts_nested_attributes_for :author
    
      # If you need to validate the associated record, you can add a method like this:
      #     validate_associated_record_for_author
      def autosave_associated_records_for_author
        # Find or create the author by name
        if new_author = Author.find_by_name(author.name)
          self.author = new_author
        else
          self.author.save!
        end
      end
    end
    

    이 코드는 테스트되지 않은,하지만 꽤 많이 당신이 필요로해야한다.

  2. ==============================

    2.팀에 선수를 추가로 생각하지 마십시오, 팀 구성원을 추가로 생각합니다. 양식은 직접 플레이어 작동하지 않습니다. 회원 모델은 PLAYER_NAME 가상 속성을 가질 수 있습니다. 무대 뒤에서이 두 선수를 조회하거나 만들 수 있습니다.

    팀에 선수를 추가로 생각하지 마십시오, 팀 구성원을 추가로 생각합니다. 양식은 직접 플레이어 작동하지 않습니다. 회원 모델은 PLAYER_NAME 가상 속성을 가질 수 있습니다. 무대 뒤에서이 두 선수를 조회하거나 만들 수 있습니다.

    class Membership < ActiveRecord::Base
      def player_name
        player && player.name
      end
    
      def player_name=(name)
        self.player = Player.find_or_create_by_name(name) unless name.blank?
      end
    end
    

    그리고 그냥 회원 양식 빌더에 PLAYER_NAME 선수의 텍스트 필드를 추가합니다.

    <%= f.text_field :player_name %>
    

    이 방법은 accepts_nested_attributes_for하는 특정하지 않고 모든 회원 형태로 사용할 수 있습니다.

    참고 : 유효성 검사가 발생하기 전에이 기술을 플레이어 모델이 생성됩니다. 이 효과를 원하지 않는 경우, 인스턴스 변수에 플레이어를 저장 한 다음 before_save 콜백에 저장.

  3. ==============================

    3.사용하는 경우 : 기존 레코드의 ID를 제출, accepts_nested_attributes_for하는 액티브 대신 새 레코드를 만드는 기존의 기록을 갱신하게됩니다. 나는 확실하지 마크 업이 비슷하지만이 같은 대략 뭔가를하려고 무엇을 해요 :

    사용하는 경우 : 기존 레코드의 ID를 제출, accepts_nested_attributes_for하는 액티브 대신 새 레코드를 만드는 기존의 기록을 갱신하게됩니다. 나는 확실하지 마크 업이 비슷하지만이 같은 대략 뭔가를하려고 무엇을 해요 :

    <%= text_field_tag "team[player][name]", current_player.name %>
    <%= hidden_field_tag "team[player][id]", current_player.id if current_player %>
    

    플레이어 이름은 ID가 공급되면 업데이트하지만, 그 생성됩니다.

    autosave_associated_record_for_ 방법을 정의하는 방식은 매우 흥미 롭다. 나는 확실히를 사용합니다! 그러나이 간단한 해결책을 고려뿐만 아니라.

  4. ==============================

    4.그냥 질문의 관점에서 라운드 것 밖으로에 프랑소와 '대답 블록으로 다시 표현 될 수 있다면, (find_or_create을 의미) :

    그냥 질문의 관점에서 라운드 것 밖으로에 프랑소와 '대답 블록으로 다시 표현 될 수 있다면, (find_or_create을 의미) :

    self.author = Author.find_or_create_by_name(author.name) unless author.name.blank?
    self.author.save! 
    
  5. ==============================

    5.당신이 has_one이나 관계 belongs_to이있는 경우이 잘 작동합니다. 하지만 통해 has_many 또는 has_many으로 부족했다.

    당신이 has_one이나 관계 belongs_to이있는 경우이 잘 작동합니다. 하지만 통해 has_many 또는 has_many으로 부족했다.

    관계를 통해 : 나는 has_many을 사용하는 태그 시스템을 가지고있다. 내가 다른 사람을 도울 수있는 솔루션을 함께했다, 그래서 갈 필요가 어디에 둘 솔루션의 날 여기 없어. 이 레일 3.2에서 테스트되었습니다.

    여기 내 모델의 기본 버전은 다음과 같습니다 :

    위치 개체 :

    class Location < ActiveRecord::Base
        has_many :city_taggables, :as => :city_taggable, :dependent => :destroy
        has_many :city_tags, :through => :city_taggables
    
        accepts_nested_attributes_for :city_tags, :reject_if => :all_blank, allow_destroy: true
    end
    

    태그 객체

    class CityTaggable < ActiveRecord::Base
       belongs_to :city_tag
       belongs_to :city_taggable, :polymorphic => true
    end
    
    class CityTag < ActiveRecord::Base
       has_many :city_taggables, :dependent => :destroy
       has_many :ads, :through => :city_taggables
    end
    

    다음과 같이 나는 실제로 autosave_associated_recored_for 메소드를 오버라이드 (override) 않았다 :

    class Location < ActiveRecord::Base
       private
    
       def autosave_associated_records_for_city_tags
         tags =[]
         #For Each Tag
         city_tags.each do |tag|
           #Destroy Tag if set to _destroy
           if tag._destroy
             #remove tag from object don't destroy the tag
             self.city_tags.delete(tag)
             next
           end
    
           #Check if the tag we are saving is new (no ID passed)
           if tag.new_record?
             #Find existing tag or use new tag if not found
             tag = CityTag.find_by_label(tag.label) || CityTag.create(label: tag.label)
           else
             #If tag being saved has an ID then it exists we want to see if the label has changed
             #We find the record and compare explicitly, this saves us when we are removing tags.
             existing = CityTag.find_by_id(tag.id)
             if existing    
               #Tag labels are different so we want to find or create a new tag (rather than updating the exiting tag label)
               if tag.label != existing.label
                 self.city_tags.delete(tag)
                 tag = CityTag.find_by_label(tag.label) || CityTag.create(label: tag.label)
               end
             else
               #Looks like we are removing the tag and need to delete it from this object
               self.city_tags.delete(tag)
               next
             end
           end
           tags << tag
         end
         #Iterate through tags and add to my Location unless they are already associated.
         tags.each do |tag|
           unless tag.in? self.city_tags
             self.city_tags << tag
           end
         end
       end
    

    위의 구현은, 삭제를 저장하고 태그를 중첩 된 형태로 fields_for을 사용할 때 필요한 방식을 변경합니다. 간단하게 할 수있는 방법이 있다면 나는 피드백에 열려있어. 이 라벨 태그 라벨을 업데이트하는 대신 변경 때 명시 적으로 태그를 변경하고 있음을 지적하는 것이 중요하다.

  6. ==============================

    6.before_validation 후크는 좋은 선택이다 : 그것은 더 애매한 autosave_associated_records_for_의 *를 오버라이드 (override)보다 간단 코드의 결과로 표준 메커니즘입니다.

    before_validation 후크는 좋은 선택이다 : 그것은 더 애매한 autosave_associated_records_for_의 *를 오버라이드 (override)보다 간단 코드의 결과로 표준 메커니즘입니다.

    class Quux < ActiveRecord::Base
    
      has_and_belongs_to_many :foos
      accepts_nested_attributes_for :foos, reject_if: ->(object){ object[:value].blank? }
      before_validation :find_foos
    
      def find_foos
        self.foos = self.foos.map do |object|
          Foo.where(value: object.value).first_or_initialize
        end
      end
    
    end
    
  7. ==============================

    7.프랑수아 보 솔레이가 굉장 @으로 답변하고 큰 문제를 해결했다. autosave_associated_record_for의 개념에 대한 자세한 내용은 좋아요.

    프랑수아 보 솔레이가 굉장 @으로 답변하고 큰 문제를 해결했다. autosave_associated_record_for의 개념에 대한 자세한 내용은 좋아요.

    그러나, 나는이 구현 한 구석 케이스를 발견했다. 새로운 작성자 이름 (A2)가 전달되는 경우 기존 포스트의 저자 (A1)의 갱신의 경우, 원래 (A1) 저자의 이름을 변경 종료됩니다.

    p = Post.first
    p.author #<Author id: 1, name: 'JK Rowling'>
    # now edit is triggered, and new author(non existing) is passed(e.g: Cal Newport).
    
    p.author #<Author id: 1, name: 'Cal Newport'>
    

    원래 코드 :

    class Post < ActiveRecord::Base
      belongs_to :author, :autosave => true
      accepts_nested_attributes_for :author
    
      # If you need to validate the associated record, you can add a method like this:
      #     validate_associated_record_for_author
      def autosave_associated_records_for_author
        # Find or create the author by name
        if new_author = Author.find_by_name(author.name)
          self.author = new_author
        else
          self.author.save!
        end
      end
    end
    

    1, 그것은 다른 블록으로 이동하고 새로 만드는 대신 그 저자를 업데이트합니다 : 편집의 경우, 게시물에 대한 self.author 이미 ID로 저자 될 것 때문이다.

    나는이 문제를 완화하기 위해 코드 (ELSIF 조건)을 변경 :

    class Post < ActiveRecord::Base
      belongs_to :author, :autosave => true
      accepts_nested_attributes_for :author
    
      # If you need to validate the associated record, you can add a method like this:
      #     validate_associated_record_for_author
      def autosave_associated_records_for_author
        # Find or create the author by name
        if new_author = Author.find_by_name(author.name)
          self.author = new_author
        elsif author && author.persisted? && author.changed?
          # New condition: if author is already allocated to post, but is changed, create a new author.
          self.author = Author.new(name: author.name)
        else
          # else create a new author
          self.author.save!
        end
      end
    end
    
  8. ==============================

    8.@ 더스틴-m의 대답은 나를 위해 쓸모 있었다 - 나는 has_many 뭔가 정의를하고있는 중이 야 : 관계를 통해. 나는 많은 아이들 (재귀)가 하나의 트렌드를 가지고 주제를 가지고있다.

    @ 더스틴-m의 대답은 나를 위해 쓸모 있었다 - 나는 has_many 뭔가 정의를하고있는 중이 야 : 관계를 통해. 나는 많은 아이들 (재귀)가 하나의 트렌드를 가지고 주제를 가지고있다.

    소스 동향 : : : 자녀 관계 검색을 통해 : 나는 표준 has_many으로이를 구성 할 때 액티브는 좋아하지 않는다. 그것은 topic.trend 및 topic.searches를 검색하지만 topic.searches.create하지 않을 것이다 (이름 : foo는).

    나는 사용자 정의 자동 저장을 구성하기 위해 위의 사용 accepts_nested_attributes_for와 올바른 결과 달성하고 그래서 : 검색, allow_destroy을 : 사실 데프 autosave_associated_records_for_searches     searches.each 할 | 의 |       s._destroy 경우         self.trend.children.delete (들)       ELSIF s.new_record?         << S self.trend.children       그밖에         s.save       종료     종료   종료

  9. from https://stackoverflow.com/questions/3579924/accepts-nested-attributes-for-with-find-or-create by cc-by-sa and MIT license