Frage

Ich erstelle ein Wiki. Jeder Artikel has_many Revisionen und ein Artikel belongs_to einem einzigen current_revision. So in der Datenbank haben die Artikel einen einzigen Hinweis auf eine ids Revision und Revisionen haben jeweils einen einzigen Hinweis auf den Artikel zu dem sie gehören. Bevor ich fortfahre, scheint dies wie eine vernünftige Art und Weise, Dinge zu tun? Es kommt mir ziemlich unorthodox, aber logisch, und ich bin nicht sicher, wie andere in ähnlichen Situationen Dinge einrichten.

Das Problem ist, dass diese Art der gegenseitigen belongs_to Beziehung scheint wirklich Rails abzuwerfen, wenn die Modelle zu schaffen. Als ich zum ersten Mal einen Artikel erstellen, ich möchte auch eine erste Revision erstellen mit ihm zu gehen.

Ich habe eine before_create Methode und tat so etwas wie:

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

, aber dies würde einen Stapelüberlauf verursacht auf Speicher, wie Rails scheinbar in einer Schleife speichern Sie die Artikel zunächst versucht, so ist es eine article_id zu Stick in der Revision hat, und dann die erste speichert die Revision, so dass es eine current_revision_id muss Stick in dem Artikel.

Wenn ich die Dinge brechen, und erstelle sie nicht gleichzeitig (aber immer noch in einer Transaktion), die erste erstellt wird nicht seine Referenzmenge. Zum Beispiel:

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

würde die Revision mit einer Null-article_id lassen, wie es die Speicherung verpaßt.

Ich glaube, ich dies eine after_create Methode umgehen könnte auch durch den Aufruf, nur die Variable mit einem Update zu initialisieren und speichern, sondern diese in ein gigantisches Chaos drehen, und ich fühle mich wie in Rails, dass in der Regel bedeutet, dass ich bin etwas falsch-headedly.

Kann mir jemand helfen, oder bin ich stecken ein wenig after_create Verfahren zu schaffen, die in der Änderung speichert?

War es hilfreich?

Lösung

habe ich ähnliches Problem vor kurzem. Sie müssen nur einen Weg der Vereinigung erklären. Kann Ihr Artikel ohne Revision erstellt werden, und dann Revision hinzugefügt bestehende Artikel?

Oder können Sie sich aus Artikel zur Revision hinweisen, die nicht wieder zeigen wird? Sollte dies nicht möglich sein, dann müssen Sie Revision als belongs_to :article und Artikel :has_many :revisions und has_one :revision, :conditions => { ... } erklären. Und Add-Flag 'Haupt Revision' zu Revisionsmodell oder bekommen letzte Revision nach Datum.

Auf diese Weise kann zyklische Abhängigkeiten nicht liefern, so sollte es leichter sein.

Edit:
Dies ist, wie ich es getestet und es funktioniert:

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

Und das ist, wie es funktioniert jetzt (Dump von Skript / Konsole):

$ ./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 |
+----+------------+--------+---------+-------------------------+-------------------------+

Uhr für Problem mit zwei Revisionen gekennzeichnet als Strom vor Reload Revisionen Sammlung auf Artikel. Wenn Sie eine der Revisionen als aktuelle markieren, dann müssen Sie Sie ganzen Artikel Objekt neu zu laden oder nur Revision Sammlung (wenn Sie current_revision Feld verwenden möchten).

Und Sie sollten wahrscheinlich behandeln current_revision nur als Nur-Lese-Zeiger. Wenn Sie versuchen, eine andere Revision es zuweisen, dann werden Sie vorherige Revision verlieren, die durch Artikel als Strom hingewiesen wurde (Rails wird alt referenzierte Objekt entfernen, wegen has_one).

Andere Tipps

Eine Revision ist nur eine Version eines Artikels, nicht wahr? Es gibt eine große Railscast auf Modell Versioning das vestal_versions Juwel verwendet, die Ihr Problem lösen sollen.

Ich denke, der beste Weg, es zu haben, ist es, jede Revision zu haben, gehören zu einem Artikel. Anstelle der zyklischen Zuordnung jeden Artikel, die zu einer Revision (Current). Verwenden Sie eine has_one Beziehung einen Artikel auf die neueste Version zu verbinden.

class Revision < ActiveRecord::Base
  belongs_to :article
  ...
end

class Article < ActiveRecord::Base
  has_many :revisions
  has_one :current_revision, :order => "version_number DESC"
  ...
end

Doch in dem Fall eines Rollbacks finden Sie die Versionsnummer von der Rückseite zu walz Revision erhöhen müssen.

Auch ... können Sie das Feld version_number und nur, um auf id wenn a.version_number > b.version_number und nur dann, wenn a.id > b.id beseitigen. Was bedeutet, dass Rollbacks in geklonten Datensätze mit höheren ids als die letzte Version führen.

Ich habe das gleiche Problem in meinem eigenen App habe, und obwohl meine Struktur etwas anders habe ich endlich eine Lösung gefunden.

In meiner app Ich habe etwas mehr wie folgt aus:

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

So habe ich ein 3-Modell Schleife statt.

In meinem Fall möchte ich die ganze Hierarchie speichern (von neu) auf einmal. Ich habe festgestellt, dass ich dies tun, indem Sie einen neuen Autor erstellen, dann das Hinzufügen der Artikel an den Autor als normal, aber wenn ich die Änderungen erstellen möchten, kann ich es wie folgt (von innerhalb der Autor Klasse):

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

(Beachten Sie, dass hier @author wurde noch entweder nicht gespeichert)

Irgendwie funktioniert dies. Ich habe in den Protokollen festgestellt, dass, Active die Revision wird das Einfügen nach der Autor und die Artikel wurden gespeichert (wie die after_create Handlers). Ich bin mir nicht sicher, warum dies anders behandelt wird, bauen zu tun, aber es scheint zu funktionieren (obwohl ich nicht überrascht wäre, wenn es keine Arbeit für jemanden anderen tut!)

Wie auch immer, ich hoffe, das hilft! (Leider ist es so lange, nachdem Sie die Frage gestellt!)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top