2017-05-19 4 views
0

非常に奇妙なレールの動作をデバッグするのに1時間を費やしました。 考える:Railsモデルのaffords 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 

アプリ/モデル/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 :tagsvideo.rbファイルでは、両方のコード例が正常に動作します。

私はレールをかなりよく知っていると思っていましたが、この動作は私にとっては本当に不明です。
Railsのバージョン:5.1.1

答えて

0

あなたは何のタグで、ここで起こっているようだが、ユーザーだけでなく、実際のビデオ記録のために作成されているこれらの2行 tag = user.tags.find_or_create_by(text: tag_text) self.tags << tag

をチェックすることをお勧めしますように思えます。しかし、タグモデルに何かがあるかどうかを見ずに知るのは難しいです。 tags関数内のタグ関連付けをユーザーと関連付けることを避けることは良いことかもしれません。

+0

私は質問を編集し、tag.rbのコンテンツを追加しました。 私にとって奇妙なのは、画像とビデオの2つの線が等しいことです。 私のアプリケーションをデバッグすると、habtmの関連付けが2つの異なる場所で2回追加されていることに気付きました。最初のレコードは、 'self.tags << tag'行が実行されているときに追加され、ビデオレコードが保存されているときに2番目のレコードが追加されています。 –

+0

私の英語のために申し訳ありません:) と言って_タグの関連付けを避けるのが良いかもしれません._あなたはhas_many:タグを別のものにリネームする必要がありますか? –

0

私は何を持っていることは、ドメインが最初の場所でより良いモデル化することができるので、XとYの問題だと思う:

# 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 

これは私たちが代わりに文字列を比較するの検索に使用することができ、正規化タグテーブルを作成し、値。 has_and_belongs_to_manyは、2つのテーブルを結合するだけで、結合テーブルを直接クエリする必要がない場合(つまり、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

関連する問題