我正在创建一个wiki。每篇文章都有多个版本,一篇文章属于一个current_revision。因此,在数据库中,Article只有一个对Revision的id的引用,而Revisions每个引用它们所属的Article。在我继续之前,这看起来像是一种理智的做事方式吗?它让我感到相当不正统,但又符合逻辑,而且我不确定处于类似情况的其他人是如何设定的。
麻烦的是,在创建模型时,这种类型的相互belongs_to关系似乎真的会抛出Rails。当我第一次创建文章时,我还想创建一个初始修订版本。
我添加了一个before_create方法并做了类似的事情:
initial_revision = self.revisions.build
self.current_revision = initial_revision
但这会在保存时导致堆栈溢出,因为Rails显然尝试在循环中首先保存文章,因此它有一个article_id粘贴在Revision中,然后首先保存Revision,所以它有一个current_revision_id到坚持文章。
当我分解并且不同时创建它们(但仍然在事务中)时,创建的第一个没有得到它的引用集。 例如:
initial_revision = Revisions.create
self.current_revision = initial_revision
initial_revision.article = self
会因为错过了保存而将版本保留为null article_id。
我想我可以通过调用after_create方法解决这个问题,只是用更新和保存初始化变量,但这变成了一个巨大的混乱,我觉得在Rails中通常意味着我是做错事。
任何人都可以提供帮助,或者我是否因为创建了一个保存更改的post_create方法而停滞不前?
答案 0 :(得分:4)
我最近遇到过类似的问题。您只需要声明一种关联方式。您的文章可以在没有修订版的情况下创建,然后将修订版添加到现有文章中吗?
或者你可以从文章指向不指向的修订版吗?如果不可能,那么您需要将修订版声明为belongs_to :article
,文章:has_many :revisions
和has_one :revision, :conditions => { ... }
。并将标记'主修订版'添加到修订模型或按日期获得最后修订。
这样您就不会提供循环依赖,因此它应该更容易。
编辑:
这就是我测试它并让它工作的方式:
class Article < ActiveRecord::Base
has_many :revisions
has_one :current_revision, :class_name => "Revision", :conditions => { :tag => "current" }
before_validation do |article|
# add current revision to list of all revisions, and mark first revision as current unless one is marked as current
article.current_revision = article.revisions.first unless article.current_revision.present?
article.revisions << article.current_revision if article.current_revision.present? and not article.revisions.member?(article.current_revision)
end
after_save do |article|
article.current_revision.mark_as_current if article.current_revision.present?
end
end
class Revision < ActiveRecord::Base
belongs_to :article
def mark_as_current
Revision.update_all("tag = ''", :article_id => self.article_id)
self.tag = "current"
save!
end
end
这就是它现在的工作方式(从脚本/控制台转储):
$ ./script/console
Loading development environment (Rails 2.3.5)
>> a1 = Article.new :name => "A1"
>> a1.revisions.build :number => 1
>> a1.save
>> a1.reload
>> a1.revisions
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | current | 2010-02-03 19:10:37 UTC | 2010-02-03 19:10:37 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
>> a1.current_revision
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | current | 2010-02-03 19:10:37 UTC | 2010-02-03 19:10:37 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
>> a1r2 = a1.revisions.build :number => 2
+------------+--------+-----+------------+------------+
| article_id | number | tag | created_at | updated_at |
+------------+--------+-----+------------+------------+
| 1 | 2 | | | |
+------------+--------+-----+------------+------------+
>> a1r2.mark_as_current
>> a1.revisions
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | current | 2010-02-03 19:10:37 UTC | 2010-02-03 19:10:37 UTC |
| 2 | 1 | 2 | current | 2010-02-03 19:11:44 UTC | 2010-02-03 19:11:44 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
>> a1.revisions.reload
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | | 2010-02-03 19:10:37 UTC | 2010-02-03 19:10:37 UTC |
| 2 | 1 | 2 | current | 2010-02-03 19:11:44 UTC | 2010-02-03 19:11:44 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
>> a1.current_revision
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | current | 2010-02-03 19:10:37 UTC | 2010-02-03 19:10:37 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
>> a1.reload
>> a1.current_revision
+----+------------+--------+---------+-------------------------+-------------------------+
| id | article_id | number | tag | created_at | updated_at |
+----+------------+--------+---------+-------------------------+-------------------------+
| 2 | 1 | 2 | current | 2010-02-03 19:11:44 UTC | 2010-02-03 19:11:44 UTC |
+----+------------+--------+---------+-------------------------+-------------------------+
在文章重新加载修订版集合之前,请注意标记为当前的两个修订版本的问题。当您将其中一个修订标记为当前版本时,则需要重新加载整个文章对象(如果要使用current_revision
字段)或仅修订版本集。
你应该只将current_revision
视为只读指针。如果您尝试为其分配另一个修订版,那么您将松开以前的修订版本,该修订版本被文章指定为当前版本(由于has_one
,Rails将删除旧的引用对象)。
答案 1 :(得分:2)
修订只是文章的一个版本,对吗?使用vestal_versions
gem在Model Versioning上有一个很好的Railscast可以解决你的问题。
答案 2 :(得分:2)
我认为获得它的最佳方法是让每个修订版属于一篇文章。而不是属于修订版(当前)的每个条款的周期性关联。使用has_one关系将文章链接到最新版本。
class Revision < ActiveRecord::Base
belongs_to :article
...
end
class Article < ActiveRecord::Base
has_many :revisions
has_one :current_revision, :order => "version_number DESC"
...
end
但是,如果发生回滚,您将增加回滚到的修订版本号。
此外...您可以删除version_number字段,只需在a.version_number > b.version_number
上输入ID,并且仅在a.id > b.id
时排序。这意味着回滚将导致克隆记录的ID比上一版本高。
答案 3 :(得分:1)
我在自己的应用程序中遇到了同样的问题,虽然我的结构略有不同,但我终于找到了解决方案。
在我的应用中,我有更多类似的内容:
class Author < ActiveRecord::Base
has_many :articles
has_many :revisions
end
class Article < ActiveRecord::Base
has_many :revisions
belongs_to :author
end
class Revision < ActiveRecord::Base
belongs_to :article
belongs_to :author
end
所以我改为使用3模型循环。
在我的情况下,我想立刻保存整个层次结构(从new)。我发现我可以通过创建一个新作者,然后像往常一样将文章添加到作者中来做到这一点,但是当我想创建修订时,我这样做(来自Author类):
def add_new_revision(@author)
article.revisions = article.revisions.push(Revision.new(:author => @author))
end
(请注意,此处@author尚未保存)
不知何故,这有效。我注意到在日志中,activerecord在作者之后插入修订版并且文章已经保存(就像使用after_create处理程序一样)。我不确定为什么这与构建它的方式不同,但它似乎有效(尽管如果它不适用于任何其他人我也不会感到惊讶!)
无论如何,我希望有所帮助! (对不起,你发布这个问题已经很久了!)