由于id = null

时间:2015-09-07 22:07:46

标签: ruby-on-rails nested-forms has-and-belongs-to-many

使用Rails 4.1.13和Ruby 2.0.0(虽然我在Ralis 4.0和Ruby 1.9.3上遇到了同样的问题。我已经阅读了很多关于这个特定问题的文章,并且无法理解为什么我的解决方案(这看起来很像{ {3}})不起作用,所以请帮助我。

我有两个模型BlogPost和Tag。 BlogPost可以包含许多标签,一个标签可以包含许多BlogPost。我通过第三个模型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

我有一个BlogPost表单,使用Formtastic,我使用以下方法为BlogPost创建复选框:

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

我遇到的问题是在添加标签之前没有保存BlogPost导致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

这也不起作用,我现在完全迷失了。

  • 最重要的是:我该怎么办?
  • 是否解决了这个问题?如果是这样,我应该如何使用它?
  • 我正确使用accepts_nested_attributes_for吗?
  • 是否与BlogPostTagRelation的命名有关(是否应该将其称为BlogPostTag?

4 个答案:

答案 0 :(得分:4)

这里的部分问题是您正在验证ID。如果id未知,Rails无法验证blog_post_id是否存在,它可以验证blog_post是否存在。

因此,答案的一部分至少是验证相关实例的存在,而不是id。

将验证更改为:

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

我也总是指定inverse_of,但我不确定它是否是这个问题的一部分。

答案 1 :(得分:1)

  1. 您的模型结构没问题。
  2. 创建帖子后,您可以通过一种方式为帖子添加标签。您只需要使用模型方法来执行此操作。您不需要<%= f.text_field :all_tags, placeholder: "Tags separated with comma" %> 。方法如下:

    在您的视图中,添加自定义属性(all_tags)。

    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.
    

    您需要在控制器中允许参数。 在Post模型中添加以下三种方法:

    nested_attributes_for

    Here's a full implementation

  3. 您正在使用>>> import nltk >>> nltk.download() showing info http://www.nltk.org/nltk_data/ True >>> from nltk import bigrams >>> bigrams(['more', 'is', 'said', 'than', 'done']) <generator object bigrams at 0x0000000002E64240> ,但在这种情况下,您的模型只有一个名称和一个belongs_to列,因此使用它是一种矫枉过正的行为。

  4. 您可以将其称为标记,但没有命名约定。如果你和其他人都能理解,那就没关系了。

答案 2 :(得分: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_tag_relations重命名为blog_posts_tags,这两个模型的字母复数组合。 Rails在后台自动查找/使用该表。它只会有关系的外键:

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) %>

答案 3 :(得分:1)

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