使用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
这也不起作用,我现在完全迷失了。
答案 0 :(得分:4)
这里的部分问题是您正在验证ID。如果id未知,Rails无法验证blog_post_id是否存在,但它可以验证blog_post是否存在。
因此,答案的一部分至少是验证相关实例的存在,而不是id。
将验证更改为:
validates :blog_post, :presence => true
validates :tag , :presence => true
我也总是指定inverse_of,但我不确定它是否是这个问题的一部分。
答案 1 :(得分:1)
创建帖子后,您可以通过一种方式为帖子添加标签。您只需要使用模型方法来执行此操作。您不需要<%= 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
您正在使用>>> 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列,因此使用它是一种矫枉过正的行为。
您可以将其称为标记,但没有命名约定。如果你和其他人都能理解,那就没关系了。
答案 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