2015-09-07 90 views
3

使用Rails 4.1.13和Ruby 2.0.0(雖然我有與Ralis 4.0和Ruby 1.9.3相同的問題。問題,並不明白爲什麼我的解決方案(這看起來像this完全一樣)不起作用,所以請幫我。Rails has_many通過無法保存在嵌套的形式由於id = null

我有兩個模型BlogPost和標籤。一個BlogPost可以有很多標籤和一個標籤可以有很多BlogPosts。我連他們通過第三個模型BlogPostRelation因此,這是我的基本設置:

# blog_post.rb 
has_many :blog_post_tag_relations, dependent: :destroy 
has_many :tags, :through => :blog_post_tag_relations 
accepts_nested_attributes_for :blog_post_tag_relations, :tags 

# tag.rb 
has_many :blog_post_tag_relations, dependent: :destroy 
has_many :blog_posts, :through => :blog_post_tag_relations 

# blog_post_tag_relation.rb 
belongs_to :tag 
belongs_to :blog_post 
validates_uniqueness_of :tag_id, :scope => [:blog_post_id] 
validates :blog_post_id, :presence => true 
validates :tag_id, :presence => true  
accepts_nested_attributes_for :tag, :blog_post 

我有博文形式,使用Formtas抽動,其中I創建使用用於博客帖子複選框:

<%= f.input :blog_title %> 
<%= f.input :tags, as: :check_boxes, :collection => tags.order(:name) %> 

我是,標籤被添加之前其導致不存在blog_post_id的驗證失敗(它是不是)博文未保存的問題:

Tag Load (1.6ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" IN (678, 56) 
    (0.9ms) BEGIN 
    BlogPost Exists (1.6ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1 
    BlogPostTagRelation Exists (1.2ms) SELECT 1 AS one FROM "blog_post_tag_relations" WHERE ("blog_post_tag_relations"."tag_id" = 678 AND "blog_post_tag_relations"."blog_post_id" IS NULL) LIMIT 1 
    CACHE (0.0ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1 
    BlogPostTagRelation Exists (1.1ms) SELECT 1 AS one FROM "blog_post_tag_relations" WHERE ("blog_post_tag_relations"."tag_id" = 56 AND "blog_post_tag_relations"."blog_post_id" IS NULL) LIMIT 1 
    CACHE (0.0ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1 
    (0.8ms) ROLLBACK 

這似乎是解決方案應該是使用inverse_of,我坦率地不明白100%。還應該提及的是,我不是100%確定如何使用accepts_nested_attributes_for來解決這類問題。我已經嘗試了所有不同的設置,但據我瞭解只有他們應該待的地方是在連接模式,BlogPostRelation,像這樣:

# blog_post_tag_relation.rb 
belongs_to :tag, :inverse_of => :blog_post_tag_relations 
belongs_to :blog_post, :inverse_of => :blog_post_tag_relations 

validates_uniqueness_of :tag_id, :scope => [:blog_post_id] 
validates :blog_post_id, :presence => true 
validates :tag_id, :presence => true  

accepts_nested_attributes_for :tag, :blog_post 

這不工作,要麼,我完全在現在失去了去做。

  • 最重要:我該怎麼辦?
  • 是否解決這個問題的inverse_of?如果是這樣,我該如何使用它?
  • 我是否正確使用accep_nested_attributes_for?
  • 是否有BlogPostTagRelation的命名做(如果它被稱爲BlogPostTag呢?

回答

3

部分原因是,你在IDS驗證。 Rails無法驗證blog_post_id是否存在,如果該ID未知,則可以使用,但可以使用驗證是否存在blog_post。

因此,至少部分答案是驗證關聯實例的存在,而不是id。

更改驗證到:

validates :blog_post, :presence => true 
validates :tag  , :presence => true  

我總是會指定inverse_of爲好,但我不知道它是這個問題的一部分。

+0

我知道這很簡單!謝謝 – Christoffer

+0

呃,這是我學到的教訓,幾乎是以同樣的方式:) –

1
  1. 你的模型結構是好的。
  2. 有一個極好的方法,你可以標記添加到您的。帖子創建後後,您只需要使用一個模型方法做that.You不需要inverse_of方法如下:。

    在您看來,添加自定義屬性(all_tags)

    <%= f.text_field :all_tags, placeholder: "Tags separated with comma" %> 
    

    您需要允許控制器中的參數。 在安置自己的模型添加這三種方法:

    def all_tags=(names) 
        self.tags = names.split(",").map do |name| 
        Tag.where(name: name.strip).first_or_create! 
    end 
    end 
        #used when the post is being created. Splits the tags and creates entries of them if they do not exist. `self.tags` ensures that they tags will be related to the post. 
    
    def all_tags 
    self.tags.map(&:name).join(", ") 
    end 
    
    #Returns all the post tags separated by comma 
    
    def self.tagged_with(name) 
        Tag.find_by_name!(name).posts 
    end 
    #Returns all the posts who also contain the tag from the current post. 
    

    Here's a full implementation

  3. 您正確使用nested_attributes_for,但在這種情況下,你有模特誰只是有一個名字和一個belongs_to的列,因此使用這是一個矯枉過正的問題。

  4. 雖然沒有關於命名的約定,但您可以將其稱爲標記。如果你和其他人能理解它,那就好了。

+2

非常感謝,但這種感覺就像一個「黑客」的東西,而不是優雅的Rails的解決方案,我期待的。我的意思是,我的問題不能那麼具體,它需要一個黑客,對吧?有沒有Rails的方式來做到這一點? – Christoffer

+0

我認爲這是處理標籤的一種相當優雅的方式。在控制器中留下更少的代碼(並且顯示出良好的編碼風格)並且通常使用更少的代碼。 –

+1

該解決方案的問題在於它不是原子的。即使驗證失敗,標籤也會被創建。 – Oleander

1

你真的想用什麼是has_and_belongs_to_many(HABTM)的關聯:http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

然而,這是假設你不想做的關係模型東西(blog_post_tag_relations你的情況)

您只需要以下模型和關聯:

class BlogPost < ActiveRecord::Base 
  has_and_belongs_to_many :tags 
end 

class Tag < ActiveRecord::Base 
  has_and_belongs_to_many :blog_posts 
end 

然後,您將必須重命名您的連接表blog_post_t ag_relations to blog_posts_tags,這兩個模型的字母數字組合。 Rails在後臺自動查找/使用該表。它只會有關係的foreign_keys:

create_table :blog_posts_tags, id: false do |t| 
    t.belongs_to :blog_post, index: true 
    t.belongs_to :tag, index: true 
end 

那麼你的表格只是工作:這裏的問題的

<%= f.input :blog_title %> 
<%= f.input :tags, as: :check_boxes, :collection => tags.order(:name) %> 
1

嘗試

validates :blog_post, :presence => true 
validates :blog_post_id, :presence => true, allow_nil: true #can ignore this 
+0

所以它應該只存在,如果它存在? :) – Oleander

相關問題