Pergunta

Estou criando um wiki. Cada artigo tem revisões e um artigo pertence a um único current_revision. Portanto, no banco de dados, os artigos têm uma única referência ao ID de uma revisão e as revisões têm uma única referência ao artigo ao qual pertencem. Antes de continuar, isso parece uma maneira sã de fazer as coisas? Isso me parece bastante pouco ortodoxo, mas lógico, e não tenho certeza de como os outros em situações semelhantes configuram as coisas.

O problema é que esse tipo de relacionamento mútuo pertence_to parece realmente desligar os trilhos ao criar os modelos. Quando criei um artigo pela primeira vez, gostaria de criar uma revisão inicial para acompanhá -lo.

Eu adicionei um método antes_create e fiz algo como:

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

Mas isso causaria um excesso de pilha no SAVE, pois o Rails aparentemente tenta em um loop para primeiro salvar o artigo, por isso tem um artigo_id para ficar na revisão e depois salvar a revisão, por isso tem um current_revision_id para aderir no Artigo.

Quando eu termino as coisas e não as cria simultaneamente (mas ainda em uma transação), o primeiro criado não obtém seu conjunto de referência. Por exemplo:

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

deixaria a revisão com um nulo artigo_id, pois perdeu o salvamento.

Eu acho que poderia contornar isso chamando um método After_Create também, apenas para inicializar a variável com uma atualização e salvar, mas isso está se transformando em uma bagunça gigantesca, e sinto que em trilhos que geralmente significam que estou fazendo algo errado -Headly.

Alguém pode ajudar, ou estou preso criando um método pequeno após_create que salva na mudança?

Foi útil?

Solução

Eu tenho um problema semelhante recentemente. Você precisa declarar apenas uma maneira de associação. Seu artigo pode ser criado sem revisão e depois a revisão adicionada ao artigo existente?

Ou você pode apontar de artigo para revisão que não está apontando de volta? Se isso não for possível, você precisa declarar revisão como belongs_to :article, e artigo :has_many :revisions e has_one :revision, :conditions => { ... }. E adicione a 'revisão principal' da bandeira para o modelo de revisão ou obtenha a última revisão por data.

Dessa forma, você não fornece dependências cíclicas, por isso deve ser mais fácil.

Editar:
Foi assim que eu testei e faço funcionar:

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

E é assim que funciona agora (despejo de script/console):

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

Observe o problema com duas revisões marcadas como atuais antes da coleta de revisões de recarregar no artigo. Quando você marca uma das revisões atuais, então você precisa recarregar seu objeto de artigo inteiro (se quiser usar current_revision campo) ou apenas coleta de revisão.

E você provavelmente deveria tratar current_revision Somente como um ponteiro somente leitura. Se você tentar atribuir outra revisão a ele, perderá a revisão anterior que foi apontada pelo artigo como atual (os trilhos removerão o objeto referenciado antigo, por causa de has_one).

Outras dicas

Uma revisão é apenas uma versão de um artigo, certo? Há um ótimo escavado ferroviário em Versão do modelo usando o vestal_versions Gem que deve resolver seu problema.

Eu acho que a melhor maneira de tê -lo é ter cada revisão pertencente a um artigo. Em vez da associação cíclica de cada artigo pertencente a uma revisão (atual). Use um relacionamento Has_One para vincular um artigo à revisão mais recente.

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

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

No entanto, no caso de uma reversão, você aumentará o número da versão da revisão revertida.

Além disso ... você pode eliminar o campo Version_Number e apenas pedir no ID se a.version_number > b.version_number e somente se a.id > b.id. O que significa que as reversão resultarão em registros clonados com IDs mais altos que a última versão.

Eu tive o mesmo problema em meu próprio aplicativo e, embora minha estrutura seja um pouco diferente, finalmente encontrei uma solução.

No meu aplicativo, tenho algo mais assim:

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

Então, eu tenho um loop de 3 modelos.

No meu caso, quero salvar toda a hierarquia (de novo) de uma só vez. Descobri que posso fazer isso criando um novo autor e adicionando os artigos ao autor normalmente, mas quando quero criar as revisões, faço isso assim (de dentro da classe do autor):

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

(Observe que aqui o @Author ainda não foi salvo) também)

De alguma forma, isso funciona. Eu notei que nos logs, o ActiveRecord está inserindo a revisão depois O autor e o artigo foram salvos (assim como o uso do manipulador de After_Create). Não sei por que isso é tratado de maneira diferente em fazer a construção, mas parece funcionar (embora eu não fique surpreso se não funcionasse para mais ninguém!)

De qualquer forma, espero que isso ajude! (Desculpe, é muito tempo depois que você postou a pergunta!)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top