Ruby on Railsの中に相互belongs_toのセットアップ
-
25-09-2019 - |
質問
私がWikiを作成しています。各記事にhas_manyの修正、および単一current_revision belongs_toの記事。だから、データベースに、記事はリビジョンのidに単一の参照を持っている、とリビジョンそれぞれは、彼らが属する記事への単一の参照を持っています。私が続ける前に、これは物事を行うにはまともな方法のように見えるのでしょうか?これはかなり異例のが、論理として私を打つ、と私は似たような状況で他の人が物事をセットアップするかどうかはわかりません。
トラブルは、相互belongs_toの関係のこのタイプは本当にモデルを作成するときにオフレールを投げるように見えるということです。私が最初の記事を作成するときに、私もそれに行くために最初のリビジョンを作成したいと思います。
私はbefore_createメソッドを追加し、のようなものでした。
をinitial_revision = self.revisions.build
self.current_revision = initial_revision
が、それはへcurrent_revision_idを持っているので、これは、リビジョンの保存最初に、それはリビジョンに固執するのarticle_idを持っているので、Railsは明らかに、記事の保存最初にループにしようとして、保存上のスタックオーバーフローを引き起こし、そしてだろう条においてスティックます。
私は物事を壊し、そしてそれらを同時に作成する(まだトランザクションで)していない、作成した最初のものは、その参照セットを取得できません。 たとえばます:
initial_revision = Revisions.create
self.current_revision = initial_revision
initial_revision.article = self
それは、保存を逃したとしては、ヌルのarticle_idでリビジョンを残してます。
私はちょうど更新して変数を初期化して保存するだけでなくafter_createメソッドを呼び出すことで、これを動き回ることができると思いますが、これは巨大な混乱になりつつある、と私は通常、私は意味していることのRailsのように感じます間違っ-headedly何かをやってます。
缶誰のヘルプ、または私が変更にセーブ少しafter_createメソッドを作成立ち往生だ?
解決
私は最近、同様の問題があります。あなたは協会の一方向のみを宣言する必要があります。あなたの記事を改正せずに作成され、その後、リビジョンは、既存の記事に追加することができますか?
それとも、戻って指してされていない記事からのリビジョンを指すことができますか?それが不可能であるべき場合は、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 |
+----+------------+--------+---------+-------------------------+-------------------------+
ウォッチの記事の現在の前にリロードリビジョンコレクションとしてマークされた二つのリビジョンでの問題のために。あなたが現在のようリビジョンの1をマークすると、あなたはあなたの記事全体のオブジェクト(あなたがcurrent_revision
フィールドを使用する場合)、またはのみリビジョンコレクションをリロードする必要があります。
そして、あなたべき唯一の読み取り専用のポインタとしておそらく御馳走current_revision
。あなたはそれに別のリビジョンを割り当てようとした場合は、(Railsがあるためhas_one
の、古い参照されたオブジェクトを削除します)、現在のような物品で指摘された以前のリビジョンを失うでしょう。
他のヒント
Aリビジョン条のちょうどバージョンは、右ですか?偉大Railscastはモデルバージョンには、あなたの問題を解決する必要がありvestal_versions
宝石を使用することにあります。
私はそれを持っているための最善の方法は、記事に属し、各リビジョンを持つことだと思います。代わりにリビジョン(現在)に属する各条の周期的な協会の。最新のリビジョンに記事をリンクするに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
ただし、ロールバックのイベントで、あなたがロールバックリビジョンのバージョン番号を増やす必要があります。
a.version_number > b.version_number
のみa.id > b.id
場合場合は、また...あなたはIDにversion_numberはフィールドとちょうど順序を排除することができます。これはロールバックが最後のバージョンよりも高いIDを持つクローン化されたレコードになりますことを意味します。
私は自分のアプリで同じ問題を持っていた、と私の構造が若干異なっているが、私は最終的に解決策を見つけた。
私のアプリでは、私はもっとこのような何かを持っています
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ハンドラを使用してのような)されています。 Iこれは、ビルドを行うに異なって扱われる理由はわからないんだけど、それが動作するようです(それは誰のために仕事をしなかった場合、私は驚かないだろうが!)
とにかく、私はそれが役に立てば幸い! (申し訳ありませんが、それはあまりにも長い間、あなたが質問を投稿した後です!)