开发者

Using before_create in Rails to normalize a many to many table

开发者 https://www.devze.com 2023-02-01 07:25 出处:网络
I am working on a pretty standard tagging implementation for a table of recipes.There is a many to many relationship between recipes and tags so the tags table will be normalized.Here are my models:

I am working on a pretty standard tagging implementation for a table of recipes. There is a many to many relationship between recipes and tags so the tags table will be normalized. Here are my models:

class Recipe < ActiveRecor开发者_JAVA百科d::Base
    has_many :tag_joins, :as => :parent
    has_many :tags, :through => :tag_joins
end

class TagJoin < ActiveRecord::Base
    belongs_to :parent, :polymorphic => true
    belongs_to :tag, :counter_cache => :usage_count
end

class Tag < ActiveRecord::Base
    has_many :tag_joins, :as => :parent
    has_many :recipes, :through => :tag_joins, :source => :parent
        , :source_type => 'Recipe'


    before_create :normalizeTable
    def normalizeTable
        t = Tag.find_by_name(self.name)
        if (t) 
            j = TagJoin.new
            j.parent_type = self.tag_joins.parent_type
            j.parent_id = self.tag_joins.parent_id
            j.tag_id = t.id
            return false
        end
    end
end

The last bit, the before_create callback, is what I'm trying to get working. My goal is if there is an attempt to create a new tag with the same name as one already in the table, only a single row in the join table is produced, using the existing row in tags. Currently the code dies with:

undefined method `parent_type' for #<Class:0x102f5ce38>

Any suggestions?

Edit

Here are my tables as well:

create_table "recipes", :force => true do |t|
    t.string   "name"
    t.text     "abstract",   :limit => 255
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
end

create_table "tag_joins", :force => true do |t|
    t.string   "parent_type"
    t.integer  "parent_id"
    t.integer  "tag_id"
    t.datetime "created_at"
    t.datetime "updated_at"
end

create_table "tags", :force => true do |t|
    t.string   "name"
    t.boolean  "is_featured"
    t.integer  "usage_count"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "category"
end


I don't see how this can work. If you add a tag through the association, the tag is created before the join table row. Also, the callback is trying to get the parent_type of a collection, which makes no sense.

I'm assuming you want something like this:

r1 = Recipe.create
r1.tags.create("chili") # tag is created

r2 = Recipe.create
r2.tags.create("chili") # existing tag is used

You could emulate this by defining a method on Recipe:

  def add_tag(tag_name)
    tags << Tag.find_or_create_by_name(tag_name)
  end

And calling it instead of .tags.create:

r1 = Recipe.create
r1.add_tag("chili") # tag is created

r2 = Recipe.create
r2.add_tag("chili") # existing tag is used


Should that be source_type, instead of parent_type? Since we don't know exactly what columns you have specified in tag_joins, that's my best guess. Rails doesn't seem to think that the column exists.

0

精彩评论

暂无评论...
验证码 换一张
取 消