Настройка взаимного own_to в Ruby on Rails
-
25-09-2019 - |
Вопрос
Я создаю вики.Каждая статья имеет_множество редакций, и статья принадлежит_одной текущей_редакции.Таким образом, в базе данных статьи имеют одну ссылку на идентификатор редакции, а каждая редакция имеет одну ссылку на статью, к которой они принадлежат.Прежде чем я продолжу, кажется ли это разумным способом сделать что-то?Мне это кажется довольно неортодоксальным, но логичным, и я не уверен, как другие в подобных ситуациях поступают.
Проблема в том, что этот тип взаимного отношения «принадлежит_к», кажется, действительно сбивает Rails с толку при создании моделей.Когда я впервые создаю статью, я хотел бы также создать для нее начальную редакцию.
Я добавил метод before_create и сделал что-то вроде:
initial_revision = self.revisions.build
self.current_revision = initial_revision
но это может вызвать переполнение стека при сохранении, так как Rails, очевидно, пытается в цикле сначала сохранить статью, поэтому у него есть item_id для прикрепления к ревизии, а затем сначала сохраняется ревизия, поэтому у него есть current_revision_id для прикрепления к ревизии. Статья.
Когда я разбиваю вещи и не создаю их одновременно (но все еще в транзакции), первый созданный объект не получает набора ссылок.Например:
initial_revision = Revisions.create
self.current_revision = initial_revision
initial_revision.article = self
оставил бы ревизию с нулевым идентификатором статьи, поскольку не сохранился.
Я думаю, что мог бы обойти это, вызвав метод after_create, просто для того, чтобы инициализировать переменную с обновлением и сохранением, но это превращается в гигантский беспорядок, и я чувствую, что в Rails это обычно означает, что я делаю что-то не так. - головой.
Может ли кто-нибудь помочь, или я застрял в создании небольшого метода after_create, который сохраняет изменения?
Решение
Недавно у меня возникла похожая проблема.Вам нужно объявить только один способ ассоциации.Может ли ваша статья быть создана без редакции, а затем добавлена редакция к существующей статье?
Или вы можете указать от статьи к редакции, которая не указывает назад?Если это невозможно, вам необходимо объявить Revision как 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 |
+----+------------+--------+---------+-------------------------+-------------------------+
Прежде чем перезагрузить коллекцию редакций в статье, обратите внимание на проблемы с двумя редакциями, помеченными как текущие.Когда вы помечаете одну из редакций как текущую, вам необходимо перезагрузить весь объект статьи (если вы хотите использовать current_revision
поле) или только сбор ревизий.
И вам, вероятно, следует лечить current_revision
только как указатель, доступный только для чтения.Если вы попытаетесь назначить ему другую ревизию, вы потеряете предыдущую ревизию, которая была указана в статье как текущая (Rails удалит старый ссылочный объект из-за has_one
).
Другие советы
Редакция - это просто версия статьи, верно? Есть отличное железнодорожное постановление на Модель версификация с использованием vestal_versions
GEM, которая должна решить вашу проблему.
Я думаю, что лучший способ иметь это, чтобы каждый пересмотр принадлежать статье. Вместо циклической ассоциации каждой статьи, принадлежащей к ревизии (току). Используйте отношения 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
Однако в случае отката вы будете увеличить номер версии ревизии обратно.
Также ... вы можете устранить поле версии_Number и просто заказать на ID, если a.version_number > b.version_number
И только если a.id > b.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-модельная петля.
В моем случае я хочу сэкономить всю иерархию (от нового) сразу. Я обнаружил, что могу сделать это, создав новый автор, затем добавляя статьи автору как обычно, но когда я хочу создать изменения, я делаю это, как это (из-за класса автора):
def add_new_revision(@author)
article.revisions = article.revisions.push(Revision.new(:author => @author))
end
(Обратите внимание, что здесь @author еще не сохранен)
Как-то это работает. Я заметил, что в журналах ActiveRecord вставляет ревизию после Автор и статья были сохранены (как правило, используя обработчик Affo_Create). Я не уверен, почему это относится к по-разному, чтобы делать постройки, но, похоже, работает (хотя я не буду удивлен, если бы не сработал для кого-либо еще!)
Во всяком случае, я надеюсь, что поможет! (Извините, это так долго после того, как вы разместили вопрос!)