2

我正在创建一个维基。每篇文章有_many Revisions,一篇文章属于_单个current_revision。因此,在数据库中,Articles 对 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

会留下一个空 article_id 的修订,因为它错过了保存。

我想我也可以通过调用 after_create 方法来解决这个问题,只是用更新和保存来初始化变量,但这变成了一个巨大的混乱,我觉得在 Rails 中这通常意味着我做错了什么——当头一棒。

任何人都可以提供帮助,还是我坚持创建一个保存更改的 after_create 方法?

4

4 回答 4

4

我最近有类似的问题。您只需要声明一种关联方式。您的文章可以在没有修订的情况下创建,然后将修订添加到现有文章中吗?

或者你能从文章指向不指向回的修订吗?如果这不可能,那么您需要将 Revision 声明为belongs_to :article、Article:has_many :revisionshas_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只将其视为只读指针。如果您尝试为其分配另一个修订版,那么您将丢失以前的修订版,该修订版被文章指出为当前(Rails 将删除旧的引用对象,因为has_one)。

于 2010-02-03T16:47:34.923 回答
2

修订只是文章的一个版本,对吗?使用gem 的模型版本控制上有一个很棒的 Railscast,vestal_versions它应该可以解决您的问题。

于 2010-02-03T16:47:43.863 回答
1

我认为最好的方法是让每个修订都属于一篇文章。而不是属于修订(当前)的每篇文章的周期性关联。使用 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 字段,并且仅在 id 时对 id 进行a.version_number > b.version_number排序a.id > b.id。这意味着回滚将导致克隆记录的 id 高于上一个版本。

于 2010-02-03T17:36:45.763 回答
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 模型循环。

就我而言,我想一次保存整个层次结构(从新的)。我发现我可以通过创建一个新作者来做到这一点,然后像往常一样将文章添加到作者,但是当我想创建修订时,我这样做(在 Author 类中):

def add_new_revision(@author)
  article.revisions = article.revisions.push(Revision.new(:author => @author))
end

(注意这里的@author 也没有被保存)

不知何故,这行得通。我注意到在日志中,activerecord在作者和文章保存插入修订(就像使用 after_create 处理程序一样)。我不确定为什么这与构建的处理方式不同,但它似乎有效(尽管如果它不适用于其他任何人,我不会感到惊讶!)

无论如何,我希望这会有所帮助!(对不起,您发布问题后很久了!)

于 2010-09-30T06:54:46.960 回答