Pregunta

Estoy creando un wiki. Cada artículo Revisiones has_many, y un artículo belongs_to una sola current_revision. Así que en la base de datos, los artículos tienen una sola referencia al ID de la revisión, y revisiones tienen cada uno una sola referencia al artículo al que pertenecen. Antes de continuar, parece esto como una manera sensata de hacer las cosas? Me parece bastante poco ortodoxo, pero lógico, y no estoy seguro de cómo otras personas en situaciones similares a preparar las cosas.

El problema es que este tipo de relación mutua belongs_to parece arrojar realmente rieles cuando la creación de los modelos. Cuando por primera vez crear un artículo, me gustaría crear también una revisión inicial para ir con ella.

He añadido un método before_create e hizo algo como:

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

pero esto podría causar un desbordamiento de pila en guardar, como carriles aparentemente trata de un bucle de primer salvamento del artículo, por lo que tiene una article_id al palo en la revisión, y luego la primera Grabar la revisión, por lo que tiene un current_revision_id a palo en el artículo.

Cuando romper las cosas, y no crean simultáneamente (pero aún en una transacción), el primero creado no obtiene su conjunto de referencia. Por ejemplo:

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

dejaría la revisión con un article_id nula, ya que perdió el proceso de almacenamiento.

Creo que podría evitar esto mediante una llamada al método after_create así, simplemente para inicializar la variable con una actualización y guardar, pero esto se está convirtiendo en un lío gigantesco, y me siento como en rieles que por lo general significa que soy haciendo algo mal-headedly.

¿Alguien puede ayudar, o estoy atascado crear un método poco after_create que salva en el cambio?

¿Fue útil?

Solución

I tiene problema similar recientemente. Es necesario declarar sólo una forma de asociación. Puede su artículo crearse sin revisión, y luego Revisión añade al artículo existente?

O puede señalar en el artículo de revisión que no se señalan de nuevo? Si eso debería ser no es posible, tiene que declarar como belongs_to :article Revisión, y el artículo :has_many :revisions y has_one :revision, :conditions => { ... }. Y la bandera complemento 'principal revisión' de revisión del modelo o conseguir la última edición de la fecha.

De esta manera usted no proporciona dependencias cíclicas, por lo que debería ser más fácil.

Editar:
Así es como he comprobado y hacer que funcione:

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

Y así es como funciona ahora (volcado desde el guión / consola):

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

Reloj para el problema con dos revisiones marcadas como corriente colección de revisiones de recarga antes sobre el artículo. Cuando se marca una de las revisiones como corriente, entonces usted necesita para recargar objeto artículo entero (si desea utilizar el campo current_revision) o sólo la colección de revisión.

Y probablemente debería current_revision tratar sólo como un puntero de sólo lectura. Si intenta asignar una nueva revisión a él, entonces perderás revisión anterior que fue señalado por el artículo como corrientes (rieles eliminará objeto referenciado de edad, a causa de has_one).

Otros consejos

Una revisión es sólo una versión de un artículo, ¿verdad? Hay una gran Railscast en Modelo de versiones utilizando la gema vestal_versions que debería resolver su problema.

creo que la mejor manera de tener que es tener cada revisión pertenecen a un artículo. En lugar de la asociación cíclica de cada artículo perteneciente a una revisión (actual). Utilice una relación has_one vincular un artículo a la última revisión.

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

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

Sin embargo, en el caso de una operación de deshacer, tendrá que aumentar el número de versión de la revisión removió a.

También ... se puede eliminar el campo version_number y orden justo en la identificación si y sólo si a.version_number > b.version_number a.id > b.id. Lo que significa que las reversiones se traducirá en los registros clonados con ID mayores que la última versión.

He tenido el mismo problema en mi propia aplicación, y aunque mi estructura es ligeramente diferente por fin he encontrado una solución.

En mi aplicación tengo algo más parecido a esto:

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

Así que tengo un bucle 3-modelo en su lugar.

En mi caso, quiero salvar a toda la jerarquía (de nuevo) a la vez. He descubierto que puedo hacer esto mediante la creación de un nuevo autor, a continuación, añadir los artículos al autor de forma normal, pero cuando quiero crear las revisiones, lo hago así (desde dentro de la clase Autor):

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

(Tenga en cuenta que aquí @author no se ha guardado todavía tampoco)

De alguna manera esto funciona. Me he dado cuenta de que en los registros, activerecord es la inserción de la revisión después y el autor del artículo se han guardado (como usar el manejador after_create). No estoy seguro de por qué esto es un trato diferente a hacer construir, pero parece que funciona (aunque no me sorprendería si no lo hizo el trabajo para nadie más!)

En fin, espero que ayude! (Siento que sea tanto tiempo después de que envió la pregunta!)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top