Я хотел бы вести учет того, когда пользователь изменяет их адрес.
Таким образом, когда заказ размещен, он будет всегда мочь сослаться на пользовательский адрес, который использовался во время размещения порядка.
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
)
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 отправил отличное решение. Плагин Версий Девственницы является отличным решением.
отрывок ниже взятого из http://github.com/laserlemon/vestal_versions
acts_as_versioned технососиской был большим запуском, но ему не удалось не отставать от введения ActiveRecord грязных объектов в версии 2.1. Кроме того, для каждой имеющей версию модели нужна ее собственная таблица версий, которая копирует большинство столбцов исходной таблицы. Таблица версий затем заполняется с записями, которые часто копируют большинство атрибутов исходной записи. В целом, не очень DRY.
vestal_versions требует только одной таблицы версий (полиморфно связанный с ее родительскими моделями) и никакие изменения вообще в существующих таблицах. Но это идет одна СУШИЛКА шага путем хранения сериализированного хеша только изменений моделей. Думайте современные системы управления версиями. Путем пересечения записи изменений модели могут вернуться к любому моменту времени.
И это, что делает vestal_versions. Мало того, что модель может вернуться к предыдущему номеру версии, но также и на дату или время!
Используйте плагин Vestal versions для этого:
См. этот экран для более подробной информации.
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
С точки зрения архитектуры данных, я предлагаю решить вашу заявленную проблему
... когда заказ будет размещен, он будет { {1}} всегда может ссылаться на адрес пользователя , который использовался во время размещения заказа .
... вы просто копируете адрес человека в модель Заказа. Элементы будут в модели OrderItem. Я бы переформулировал проблему следующим образом: «Заказ происходит в определенный момент времени. OrderHeader включает в себя все соответствующие данные в этот момент времени».
Это ненормально?
Нет, потому что OrderHeader представляет собой момент времени, а не текущая «правда».
Вышеупомянутый стандартный способ обработки данных заголовка заказа устраняет большую сложность вашей схемы, в отличие от отслеживания всех изменений в модели.
- Придерживайтесь решения, которое решает настоящую проблему, а не возможные проблемы - кому-нибудь нужна история изменений пользователя? Или вам просто нужны заголовки заказа, чтобы отразить реальность самого заказа?
Добавлено: Обратите внимание, что вам нужно знать , на какой адрес в конечном итоге был отправлен заказ / счет. Вы не хотите смотреть на старый заказ и видеть текущий адрес пользователя, вы хотите увидеть адрес, который заказ использовал при отправке заказа. См. Мой комментарий ниже, чтобы узнать больше об этом.
Помните, что, в конечном счете, цель системы - моделировать реальный мир. В реальном мире, когда заказ распечатан и отправлен вместе с заказанными товарами, адрес доставки заказа больше не меняется. Если вы отправляете программные товары или услуги, вам нужно экстраполировать из более простого примера.
Системы заказов - отличный случай, когда очень важно понимать потребности и реалии бизнеса - не просто разговаривайте с бизнес-менеджерами, также разговаривайте с непосредственными продавцами, заказывайте клерки, клерки дебиторской задолженности, сотрудники отдела отгрузки и т. д.
Я думаю, это будет так просто:
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
Затем, когда пользователь меняет свой адрес, вы выполняете своего рода «логическое удаление» на старом data, создав новый UserAddress и установив поле "previous_address_id" как указатель на старые данные. Это устраняет необходимость в вашей таблице карты и создает своего рода связанный список . Таким образом, всякий раз, когда размещается заказ, вы связываете его с определенным UserAddress, который гарантированно никогда не изменится.
Еще одним преимуществом этого является то, что он позволяет вам отслеживать изменения адреса пользователей, что-то вроде рудиментарного регистратора.
Вы ищете плагин 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
- это метод, добавленный в модель аудита с помощью acts_as_audited. Он берет временную метку и восстанавливает модель в том виде, в котором она была в этот момент времени. Я полагаю, что он собирает ревизию из аудитов, проведенных в этой конкретной модели до указанного времени. Поэтому не имеет значения, совпадает ли updated_at в заказе с точным временем.