2

ウィキを作成しています。各記事には多くのリビジョンがあり、記事は単一の現在のリビジョンに属します。したがって、データベースでは、記事はリビジョンの ID への参照を 1 つ持ち、各リビジョンは所属する記事への参照を 1 つ持ちます。先に進む前に、これは物事を行うための正気な方法のように思えますか? それはかなり非正統的ですが、論理的だと思います。同様の状況にある他の人がどのように設定したかはわかりません.

問題は、このタイプの相互の belongs_to 関係が、モデルを作成するときに Rails を実際に混乱させているように見えることです。私が最初に記事を作成するとき、それに合わせて最初のリビジョンも作成したいと思います。

before_create メソッドを追加して、次のようにしました。

initial_revision = self.revisions.build
self.current_revision = initial_revision

しかし、これは保存時にスタック オーバーフローを引き起こします。Rails はループ内で最初にアーティクルを保存しようとするようです。論文。

私が物事を分割し、それらを同時に作成しない場合 (ただし、トランザクション内で)、最初に作成されたものは参照セットを取得しません。例えば:

initial_revision = Revisions.create
self.current_revision = initial_revision
initial_revision.article = self

保存に失敗したため、リビジョンの article_id が null のままになります。

update と save で変数を初期化するだけで、after_create メソッドを呼び出すことでこれを回避できると思いますが、これは巨大な混乱に変わりつつあり、Rails では通常、何か間違ったことをしていることを意味するように感じます。 -頭がおかしい。

誰か助けてもらえますか、それとも変更を保存する after_create メソッドを少し作成するのに行き詰まっていますか?

4

4 に答える 4

4

私は最近同様の問題を抱えています。関連付けの方法を 1 つだけ宣言する必要があります。リビジョンなしで記事を作成し、既存の記事にリビジョンを追加することはできますか?

または、アーティクルからリビジョンを指すことができますか? それが不可能な場合は、 Revision をbelongs_to :article、 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 |
+----+------------+--------+---------+-------------------------+-------------------------+

アーティクルのリビジョン コレクションをリロードする前に、最新としてマークされた 2 つのリビジョンの問題に注意してください。current_revisionリビジョンの 1 つを最新としてマークすると、記事オブジェクト全体 (フィールドを使用する場合) またはリビジョン コレクションのみをリロードする必要があります。

current_revisionまた、おそらく読み取り専用ポインターとしてのみ扱う必要があります。それに別のリビジョンを割り当てようとすると、記事で現在として指摘されていた以前のリビジョンが失われます (Rails は のために古い参照オブジェクトを削除しますhas_one)。

于 2010-02-03T16:47:34.923 に答える
2

リビジョンは単に記事のバージョンですよね? あなたの問題を解決するはずのgemを使用したモデルのバージョン管理に関する優れたRailscastがあります。vestal_versions

于 2010-02-03T16:47:43.863 に答える
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 に答える
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 で注文することもできa.version_number > b.version_numberますa.id > b.id。つまり、ロールバックにより、最後のバージョンよりも高い ID を持つクローン レコードが作成されます。

于 2010-02-03T17:36:45.763 に答える