2017-05-19 3 views
0

아주 이상한 레일 동작을 디버깅하는 데 한 시간을 보냈습니다. 감안할 때 :Rails 모델에서 HABTM 동작을 '포함'하는 이유는 무엇입니까?

응용 프로그램/모델/user.rb

class User < ApplicationRecord 
    ... 
    has_many :images 
    has_many :videos 
    ... 
    has_many :tags 
    ... 
end 


응용 프로그램/모델/image.rb

class Image < ApplicationRecord 
    ... 
    belongs_to :user 
    ... 
    has_and_belongs_to_many :tags 
    ... 
    include TagsFunctions 
    ... 
end 


응용 프로그램/모델/video.rb

class Video < ApplicationRecord 
    ... 
    include TagsFunctions 
    ... 
    belongs_to :user 
    ... 
    has_and_belongs_to_many :tags 
    ... 
end 

app/models 나는 그런 코드를 실행하면 /tag.rb

class Tag < ApplicationRecord 
    belongs_to :user 

    validates :text, uniqueness: {scope: :user}, presence: true 

    before_create :set_code 

    def set_code 
    return if self[:code].present? 

    loop do 
     self[:code] = [*'A'..'Z'].sample(8).join 
     break if Tag.find_by(code: self[:code]).nil? 
    end 
    end 
end 


응용 프로그램/모델/문제/tags_functions.rb는

module TagsFunctions 
    extend ActiveSupport::Concern 

    # hack for new models 
    included do 
    attr_accessor :tags_after_creation 

    after_create -> { self.tags_string = tags_after_creation if tags_after_creation.present? } 
    end 

    def tags_string 
    tags.pluck(:text).join(',') 
    end 

    def tags_string=(value) 
    unless user 
     @tags_after_creation = value 
     return 
    end 

    @tags_after_creation = '' 
    self.tags = [] 
    value.to_s.split(',').map(&:strip).each do |tag_text| 
     tag = user.tags.find_or_create_by(text: tag_text) 
     self.tags << tag        
    end 
    end 
end 


:

user = User.first 
tags_string = 'test' 
image = user.images.create(tags_string: tags_string) 
video = user.videos.create(tags_string: tags_string) 

그것은 1 개 항목을 줄 것이다 image.tags, 그러나 중복 항목 2 개는 video.tags

우리는 코드 이런 식으로 변경하면

그러나 :

user = User.first 
tags_string = 'test' 
image = Image.create(user: user, tags_string: tags_string) 
video = Video.create(user: user, tags_string: tags_string) 

모든, 이미지에 대한 1 개 태그 및 비디오 더

그리고 ... 1 개 태그를 잘 작동을 우리는 include TagsFunctions아래로 이동하는 경우has_and_belongs_to_many :tags, video.rb 파일에서 두 코드 예제 모두 정상적으로 작동합니다.

나는 레일을 잘 알고 있다고 생각했지만,이 행동은 나에게 정말로 불분명하다.
레일 버전 : 5.1.1

답변

0

당신이 여기에서 일어나고있는 것 같다 무엇 tag = user.tags.find_or_create_by(text: tag_text) self.tags << tag

이 태그는 사용자에 대해 생성되고있는 것입니다이 2 줄뿐만 아니라 실제 영상 기록을 확인 할 수 있습니다 것 같아 . 그러나 태그 모델에 아무것도 없는지 알기가 어렵습니다. 태그 함수에서 태그 연관을 사용자와 연관시키는 것을 피하는 것이 좋습니다. 이것은 우리가 비교 문자열 대신 조회에 사용할 수있는 표준화 된 태그 테이블을 작성

# rails g model tag name:string:uniq 
class Tag < ApplicationRecord 
    has_many :taggings 
    has_many :tagged_items, through: :taggings, source: :resource 
    has_many :videos, through: :taggings, source: :resource, source_type: 'Video' 
    has_many :images, through: :taggings, source: :resource, source_type: 'Image' 
end 

# rails g model tagging tag:belongs_to tagger:belongs_to resource:belongs_to:polymorphic 
class Tagging < ApplicationRecord 
    belongs_to :tag 
    belongs_to :tagger, class_name: 'User' 
    belongs_to :resource, polymorpic: true 
end 

class User < ApplicationRecord 
    has_many :taggings, foreign_key: 'tagger_id' 
    has_many :tagged_items, through: :taggings, source: :resource 
    has_many :tagged_videos, through: :taggings, source: :resource, source_type: 'Video' 
    has_many :tagged_images, through: :taggings, source: :resource, source_type: 'Image' 
end 

module Taggable 
    extend ActiveSupport::Concern 

    included do 
    has_many :taggings, as: :resource 
    has_many :tags, through: :taggings 
    end 

    # example 
    # @video.tag!('#amazeballs', '#cooking', tagger: current_user) 
    def tag!(*names, tagger:) 
    names.each do |name| 
     tag = Tag.find_or_create_by(name: name) 
     taggnings.create(tag: tag, tagger: tagger) 
    end 
    end 
end 

:

+0

나는 편집 한 질문과 tag.rb 내용을 추가했습니다. 저에게 이상한 점은 이미지와 비디오에서 두 줄이 동일하다는 것입니다. 내 응용 프로그램을 디버깅 할 때 habtm 연관이 두 곳에서 두 번 추가되고있는 것으로 나타났습니다. 첫 번째 레코드는'self.tags << tag' 라인이 실행될 때 추가되고 비디오 레코드가 저장 될 때 두번째 레코드가 추가 될 때 추가됩니다. –

+0

내 영어로 죄송합니다 :) 말하기로 _ 태그 기능에서 사용자와 태그 연결을 연관시키는 것을 피할 수 있습니다 ._ 내가 has_many : 태그의 이름을 다른 것으로 변경해야한다는 것을 의미합니까? –

0

나는 도메인이 처음부터 더 나은 모델링 할 수 있기 때문에 당신이있는 것은 X와 Y의 문제라고 생각합니다 값. has_and_belongs_to_many은 두 테이블을 조인하는 중 가장 간단한 경우에만 유용하며 조인 테이블을 직접 쿼리 할 필요가 없습니다. 즉, HABTM를 사용하면 실수로 90 %의 실수가 발생합니다.

콜백을 사용하여 HABTM을 "해킹"하면 악화됩니다.

다른 태그 가능 클래스를 설정할 때 약간의 메타 프로그래밍을 사용하여 복제를 줄일 수 있습니다.

class Video < ApplicationRecord 
    include Taggable 
end 

class Image< ApplicationRecord 
    include Taggable 
end 
+0

20 분 내에 쓴이 코드보다 100 배나 더 잘 할 가능성이있는 몇 가지 라이브러리가 있습니다. – max

+0

코드를 보내 주셔서 감사합니다. 내 해킹의 목적은'create' 메소드에서 모든 태그를 전달하는 레코드를 생성하는 것입니다. 그렇지 않으면 모델 생성 후 태그를 추가해야합니다. 내 별도의 테이블이 필요하지 않습니다. 내 조인 테이블은 절대로 직접 쿼리되지 않으며 관계도 매우 간단합니다. 왜 그 이상한 행동을했는지 궁금 해서요. 그리고 도메인에 대한 첫 번째 구절을 이해하지 못했습니다. –

+0

현실을 모델에 매핑하는 작업을 잘못 처리했습니다. 'accepts_nested_attributes_for'를 사용하고 컨트롤러에서 태그의 문자열을 분리/파싱하는 것이 훨씬 덜 해킹 된 방식으로 그렇게 할 수 있습니다. – max