Rails:存储用户设置的最佳实践是什么?

27

我想知道存储用户设置的最佳方法是什么?对于 Web 2.0 应用程序,我希望用户能够选择某些设置。目前只是要让他们决定何时接收电子邮件通知。

最简单的方法是创建一个名为“设置(Settings)”的模型,并为每个设置添加一列,然后与用户建立1:1的关系。

但是,是否有更好的解决方案?将信息存储在用户表本身中是否更好?或者应该使用含有“设置名称(settings_name)”和“设置值(settings_value)”的表来完全公开存储的设置类型(而无需在添加选项时运行任何迁移)?

您的意见是什么?

谢谢!

5个回答

13
如果你使用PostgreSQL,最好的解决方案是使用https://github.com/diogob/activerecord-postgres-hstore/。这是一种简单、快速且可靠的在数据库中存储哈希的方式。由于它不仅仅是一个序列化的文本字段,你也可以在上面进行搜索,并且不需要像HasEasy那样创建新的表格。请注意保留HTML标签。
def User
  serialize :preferences, ActiveRecord::Coders::Hstore
end

user = User.create preferences: { theme: "navy" }
user.preferences['theme']

8
在Rails 4中,你不再需要gem了!查看 这个教程 - fotanus

11
如果用户设置不希望被查找(例如通过 User.find_by_x_preference),您也可以将它们存储在序列化列中作为哈希表。这是Rails文档中描述的用例(http://www.railsbrain.com/api/rails-2.3.2/doc/index.html?a=M002334&name=serialize#)。
class User < ActiveRecord::Base
  serialize :preferences
end

u = User.new
u.preferences = {:favorite_color => "green", :favorite_book => "Moby Dick"}

2
代码中这部分实现很好,但它是“查找+解析”,而不仅仅是“查找”。如果你经常使用这些设置来控制应用逻辑,那么这可能会减慢你的速度。 - brittohalloran
如果这个能够很好地与表单构建器集成,那就太棒了。 - lulalala
3
我在使用这种技术时遇到的问题是,当您添加新的偏好设置,例如 :favorite_food => pizza 时,没有简单的方法为所有用户设置默认设置,如果有一个直接的数据库列,则可以实现此功能。 - earnold
@earnold,你可以在加载设置时创建一个方法,检查新设置是否存在,否则添加并保存它。虽然不太美观,但这是一种解决方法。 - Dofs

11

“开放”表格方法使得使用AR对其进行建模变得困难,因为您必须担心数据类型(布尔值,整数,字符串等)。我总是将偏好设置添加为用户表上的列,如果它们太多,则将它们移动到一个用户首选项表中。这很简单,易于使用。


这是我正在建模的一个项目,但我想知道,你如何访问全站用户首选项?我已经阅读了很多关于在模型中尝试访问当前用户是不好的帖子,所有会话数据都应该在控制器中管理。但在我的情况下,我有依赖于当前用户的模型/视图。例如,如果当前用户正在浏览汽车,则我始终希望根据当前用户设置显示汽车油箱大小为“升”或“加仑”。每次我想看到@car.tank_size(current_user)时,这真的有必要吗? - FireDragon

7

我们使用了一个有用的插件叫做HasEasy。它将数据存储在一个竖直表中,但允许您添加验证、前/后存储处理、类型等内容。


听起来很有趣,但你发布的链接已经失效了。而且谷歌也没有帮助到我。你有在哪里找到这个插件的提示吗? - Ole Spaarmann
1
好的,我不知道为什么链接第一次不起作用。在收到“页面未找到”后刷新页面,它就可以正常访问了。 - scottd
1
@tokland,我原本以为“maintained”意味着每周或每两周更新一次。但它已经四年没有更新了。 - FloatingRock

3
Rails 6支持ActiveRecordStore,可用于解决此问题。通过添加类型定义来改进ActiveRecordStore的宝石是activerecord-typedstore
在您的模型中定义属性:
class User < ApplicationRecord
  typed_store :settings do |s|
    s.boolean :public, default: false, null: false
    s.string :email
    s.datetime :publish_at
    s.integer :age, null: false
  end
end

而且您可以使用它:

shop = Shop.new(email: 'george@cyclim.se')
shop.public?        # => false
shop.email          # => 'george@cyclim.se'
shop.published_at   # => nil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接