如何使用Ruby on Rails中的映射表跟踪模型历史记录?

时间:2010-02-24 03:33:02

标签: ruby-on-rails activerecord schema

梦想

我想记录用户何时更改地址。

这样,当下订单时,它将始终能够引用订单下达时使用的用户地址。

可能的架构

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 plugin 是一个出色的解决方案

以下摘录自http://github.com/laserlemon/vestal_versions

最后,DRY ActiveRecord版本化!

  acts_as_versionedtechnoweenie是一个很好的开始,但它无法跟上ActiveRecord在2.1版中引入的脏对象。此外,每个版本化的模型都需要自己的版本表,该表复制了大多数原始表的列。然后在版本表中填充通常复制大多数原始记录属性的记录。总而言之,不是很干。

     

vestal_versions只需要一个版本表(与其父模型多态关联),并且不需要对现有表进行任何更改。但它通过存储模型的变化的序列化哈希来实现DRYer的一步。想想现代版控制系统。通过遍历变更记录,模型可以恢复到任何时间点。

     

而这正是vestal_versions所做的。 不仅可以将模型恢复为以前的版本号,还可以恢复为日期或时间!

5 个答案:

答案 0 :(得分:13)

使用Vestal versions plugin

有关详细信息,请参阅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关联,保证永远不会更改。

这样做的另一个好处是,它允许您跟踪用户地址的更改,有点像基本的记录器。