Как отслеживать историю моделей с помощью таблицы сопоставлений в Ruby on Rails? - PullRequest
9 голосов
/ 24 февраля 2010

мечта

Я хотел бы вести учет того, когда пользователь меняет свой адрес.

Таким образом, при размещении заказа он всегда сможет ссылаться на адрес пользователя, который использовался во время размещения заказа.

возможная схема

users (
  id
  username
  email
  ...
)

user_addresses (
  id
  label
  line_1
  line_2
  city
  state
  zip
  ...
)

user_addresses_map (
  user_id
  user_address_id
  start_time
  end_time
)

orders (
  id
  user_id
  user_address_id
  order_status_id
  ...
  created_at
  updated_at
)

в sql, это может выглядеть примерно так: [sql]

select ua.*

from  orders    o

join  users     u
  on  u.id = o.user_id

join  user_addressses_map   uam
  on  uam.user_id = u.id
  and uam.user_address_id = o.user_address_id

join  user_addresses        ua
  on  ua.id = uam.user_address_id
  and uam.start_time < o.created_at
  and (uam.end_time >= o.created_at or uam.end_time is null)
;

редактировать: решение

@ KandadaBoggu опубликовал отличное решение. Плагин Vestal Versions является отличным решением .

фрагмент ниже взят из http://github.com/laserlemon/vestal_versions

Наконец, СУХОЕ управление версиями ActiveRecord!

acts_as_versioned от technoweenie было отличным началом, но ему не удалось поспеть за введением ActiveRecord грязных объектов в версии 2.1. Кроме того, каждая версионная модель нуждается в собственной таблице версий, которая дублирует большинство столбцов исходной таблицы. Затем таблица версий заполняется записями, которые часто дублируют большинство атрибутов исходной записи. В общем, не очень СУХОЙ.

vestal_versions требуется только одна таблица версий (полиморфно связанная с ее родительскими моделями) и никаких изменений в существующих таблицах. Но это делает DRYer одним шагом, сохраняя сериализованный хеш только изменений моделей. Подумайте о современных системах контроля версий. Просматривая запись изменений, модели могут быть возвращены в любой момент времени.

И это именно то, что делает vestal_versions. Не только модель может быть возвращена к предыдущему номеру версии, но также к дате или времени!

Ответы [ 5 ]

13 голосов
/ 24 февраля 2010

Используйте для этого плагин Vestal :

Подробнее см. этот .

class Address < ActiveRecord::Base
  belongs_to :user
  versioned
end


class Order < ActiveRecord::Base
   belongs_to :user

   def address
     @address ||= (user.address.revert_to(updated_at) and user.address)
   end
end
8 голосов
/ 16 октября 2014

Думаю, я бы добавил обновленный ответ. Похоже, гем paper_trail стал самым популярным для создания версий в Rails. Он также поддерживает Rails 4.

https://github.com/airblade/paper_trail

Из их readme:

Для настройки и установки:

gem 'paper_trail', '~> 3.0.6'
bundle exec rails generate paper_trail:install
bundle exec rake db:migrate

Основное использование:

class Widget < ActiveRecord::Base
  has_paper_trail
end

Для конкретного экземпляра класса Widget:

v = widget.versions.last
v.event                     # 'update' (or 'create' or 'destroy')
v.whodunnit                 # '153'  (if the update was via a controller and
                               #         the controller has a current_user method,
                               #         here returning the id of the current user)
v.created_at                # when the update occurred
widget = v.reify            # the widget as it was before the update;
                               # would be nil for a create event

Я только играл с ним, но я собираюсь запустить довольно амбициозный сайт, который потребует хорошего управления версиями определенных классов, и я решил использовать paper_trail.

=== РЕДАКТИРОВАТЬ ====

Я внедрил драгоценный камень paper_trail в производство на сайте www.muusical.com, и он хорошо работал, используя описанное выше. Единственное изменение заключается в том, что я использую gem 'paper_trail', '~> 4.0.0.rc' в моем Gemfile.

4 голосов
/ 24 февраля 2010

С точки зрения архитектуры данных, я предлагаю решить указанную вами проблему

... при размещении заказа он будет всегда иметь возможность ссылаться на пользователя адрес, который был использован во время размещение заказа.

... вы просто копируете адрес человека в модель заказа. Элементы будут в модели OrderItem. Я бы переформулировал эту проблему следующим образом: «Заказ происходит в определенный момент времени. В OrderHeader включены все соответствующие данные на данный момент времени».

Это ненормально?

Нет, потому что OrderHeader представляет момент времени, а не текущую "правду".

Выше приведен стандартный способ обработки данных заголовка заказа, который значительно усложняет вашу схему, а не отслеживает все изменения в модели.

- Придерживайтесь решения, которое решает реальную проблему, а не возможные проблемы - кому-нибудь нужна история изменений пользователя? Или вам просто нужны заголовки заказа, чтобы отразить реальность самого заказа?

Добавлено: И обратите внимание, что вам нужно знать , какой адрес в конечном итоге использовался для отправки заказа / счета. Вы не хотите просматривать старый заказ и видеть текущий адрес пользователя, вы хотите видеть адрес, который использовался в заказе при отправке заказа. Смотрите мой комментарий ниже для получения дополнительной информации по этому вопросу.

Помните, что, в конечном счете, целью системы является моделирование реального мира. В реальном мире, после того, как заказ распечатан и отправлен с заказанным товаром, доставка заказа больше не меняется. Если вы отправляете мягкие товары или услуги, вам нужно экстраполировать из более простого примера.

Системы заказов - это отличный случай, когда очень важно понимать потребности и реалии бизнеса - не просто разговаривать с бизнес-менеджерами, но и разговаривать с руководителями отдела продаж, заказ клерки, клерки по дебиторской задолженности, сотрудники отделов доставки и т. д.

2 голосов
/ 24 февраля 2010

Вы ищете плагин acts_as_audited . Он предоставляет таблицу аудита и модель для использования вместо вашей карты.

Чтобы настроить его, запустите миграцию и добавьте в свою модель адресов следующее:

class UserAddress < ActiveRecord::Base
  belongs_to :user
  acts_as_audited
end

После того, как вы его настроите, все, что вам нужно сделать, это определить адресный метод в заказе. Примерно так:

class Order < ActiveRecord::Base
   belongs_to :user
   attr_reader :address

   def address
     @address ||= user.user_address.revision_at(updated_at)
   end
end

И вы можете получить доступ к адресу пользователя в момент завершения заказа с помощью @order.address

revision_at - это метод, добавляемый в проверенную модель с помощью activ_as_audited. Требуется временная метка и восстанавливает модель, как это было в тот момент времени. Я полагаю, что это объединяет пересмотр аудиторских проверок этой конкретной модели до наступления определенного времени. Поэтому не имеет значения, соответствует ли updated_at в заказе точному времени.

0 голосов
/ 24 февраля 2010

Я думаю, что это будет так просто:

Users:
  id
  name
  address_id

UserAddresses:
  id
  user_id
  street
  country
  previous_address_id

Orders
  id
  user_id #to get the users name
  user_address_id #to get the users address

Затем, когда пользователь меняет свой адрес, вы выполняете своего рода «логическое удаление» старых данных, создавая новый UserAddress и устанавливаяполе «предыдущий_адрес_идентификатора» будет указателем на старые данные.Это устраняет необходимость в вашей таблице карт и создает своего рода связанный список .Таким образом, всякий раз, когда размещается заказ, вы связываете его с определенным UserAddress, который гарантированно никогда не изменится.

Еще одно преимущество этого состоит в том, что он позволяет отслеживать изменения адреса пользователя,как элементарный регистратор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...