I have seen two different approaches in saving user preferences.
APPROACH 1:
Serializing them and saving in one of the column of USERS table
APPROACH 2:
Creating a separate table PREFERENCES and make a has_many association from USERS to PREFERENCES.
Which one of the above two approaches would you prefer and what are the pros and cons of each over other?
It's usually a good idea to favor normalization. The second solution keeps your models cleaner, allows for easy extensibility if new preferences are added, and keeps your tables uncluttered.
I grappled with this same question so I thought I'd share what I found in a "community wiki" answer.
Serializing in a single attribute
Simple user preferences for your Rails app is a blog post describing how to do this.
Edit a serialized hash in a form? describes how to edit such a hash in a form.
A helpful trick is to make the form from OpenStruct.new(@user.preferences)
hash to automatically make accessor methods for each hash attribute.
DYE/has_serialized - GitHub lets you treat those attributes in the serialized hash as attributes on the (user) model.
Preferences in a separate table
Best practice to store user settings? has some tips. Below are some libs including two from another answer by @hopeless.
- rails-settings manages a table of key/value pairs like a Hash stored in you database, using simple ActiveRecord like methods for manipulation. You can store any kind of object: Strings, numbers, arrays, or any object which can be noted as YAML. (Tested with Rails 3.1 and newer including Rails 4.x and Rails 5.x)
- Preference-fu is good for simple boolean preferences, uses a single column for multiple preferences.(last updated 2009)
- Preferences is more flexible, uses a separate table, some nice syntactic sugar. (last updated 2011)
- HasEasy stores the data in a vertical table, but allows you to add validations, pre/post storage processing, types, etc. (Last updated 2008)
You can also try using metaprogramming:
Practical Metaprogramming with Ruby: Storing Preferences
An improved version of the first approach can be had if you're on PostgreSQL 9.2/3+ and Rails 4+. You can use store_accessor
to store preferences in a PostgreSQL hstore column with support for validations and querying.
class User
store_accessor :preferences, :receive_newsletter
validates :receive_newsletter, presence: true
end
user.receive_newsletter => 'true'
User.where("preferences->'receive_newsletter' = 'true'")
See http://mikecoutermarsh.com/using-hstore-with-rails-4/ for more details (migrations) and a special note on handling booleans.
Approach 2
You can add preferences, without cluttering up the user table
There are some Rails plugins to handle this usecase:
- Preference-fu (good for simple
boolean preferences, uses a single column for multiple preferences)
- Preferences
(more flexible, uses a separate table, some nice syntactic sugar)
I'd approach 2 because it is cleaner and easier to update. You will be able to add more preferences as complex as you want.
It will be a bit slower since you have a join to do, but it'll be worth it
In 2016, I would back Option 2.
Why?
User settings tend to become a core piece of every application. If they are retrieved on every request, you’re now making an extra query with each request. Using a separate table makes sense when you have to have individual columns for each setting. But since we’re using jsonb, that’s not an issue. It’s just a single column.
Read more