我想记录用户何时更改地址。
这样,当下订单时,它将始终能够引用订单下达时使用的用户地址。
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中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 plugin 是一个出色的解决方案。
以下摘录自http://github.com/laserlemon/vestal_versions
acts_as_versioned的technoweenie是一个很好的开始,但它无法跟上ActiveRecord在2.1版中引入的脏对象。此外,每个版本化的模型都需要自己的版本表,该表复制了大多数原始表的列。然后在版本表中填充通常复制大多数原始记录属性的记录。总而言之,不是很干。vestal_versions只需要一个版本表(与其父模型多态关联),并且不需要对现有表进行任何更改。但它通过存储仅模型的变化的序列化哈希来实现DRYer的一步。想想现代版控制系统。通过遍历变更记录,模型可以恢复到任何时间点。
而这正是vestal_versions所做的。 不仅可以将模型恢复为以前的版本号,还可以恢复为日期或时间!
答案 0 :(得分:13)
有关详细信息,请参阅this屏幕广播。
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 :(得分:8)
我想添加一个更新后的答案。似乎paper_trail
gem已成为Rails中最受欢迎的版本。它也支持Rails 4.
https://github.com/airblade/paper_trail
从他们的自述文件中可以看出:
设置和安装:
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
。
=== EDIT ====
我已经在www.muusical.com上实现了paper_trail
生产中的宝石,并且使用上述方法效果很好。唯一的变化是我在gem 'paper_trail', '~> 4.0.0.rc'
中使用Gemfile
。
答案 2 :(得分:4)
从数据架构的角度来看,我建议解决你陈述的问题
...当下订单时,它会 始终能够引用用户 在时间使用的地址 下订单。
...您只需将此人的地址复制到订单模型中即可。这些项目将在OrderItem模型中。我会将问题重新表述为“订单在某个时间点发生。订单主页包含该时间点的所有相关数据。”
不正常吗?
不,因为OrderHeader代表一个时间点,而不是持续的“真相”。
以上是处理订单标题数据的标准方法,从模式中移除了很多复杂性,而不是跟踪模型中的所有更改。
- 坚持解决真正问题的解决方案,而不是可能的问题 - 是否有人需要用户更改的历史记录?或者您是否只需要订单标题来反映订单本身的实际情况?
已添加:请注意,您需要知道哪个地址最终用于发送订单/发票。您不希望查看旧订单并查看用户的当前地址,您希望查看订单发货时订单使用的地址。有关详细信息,请参阅下面的评论。
请记住,最终,系统的目的是模拟现实世界。在现实世界中,一旦订单打印出来并与订购的商品一起发送,订单的发货地点就不会再变化。如果您要发送软商品或服务,那么您需要从更简单的示例中进行推断。
订单系统是一个很好的案例,对于了解业务需求和现实非常重要 - 不要只是与业务经理交谈,还要与一线销售人员交谈,订单文员,应收账款文员,运输部门人员等
答案 3 :(得分:2)
您正在寻找acts_as_audited plugin。它提供了一个审核表和模型,用于代替您的地图。
要进行设置,请运行迁移并将以下内容添加到您的用户地址模型中。
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是否与时间完全匹配并不重要。
答案 4 :(得分:0)
我认为这很简单:
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对旧数据执行某种“逻辑删除”,并将“previous_address_id”字段设置为指向旧数据的指针。这消除了对地图表的需要,并创建了一种linked list。通过这种方式,无论何时下订单,您都可以将其与特定的UserAddress关联,保证永远不会更改。
这样做的另一个好处是,它允许您跟踪用户地址的更改,有点像基本的记录器。