48

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?

tpower
  • 56,100
  • 19
  • 68
  • 100
Dharam Gollapudi
  • 6,328
  • 2
  • 34
  • 42
  • What type of preference are you storing? Boolean? Multiple items? – ideasasylum Jul 03 '09 at 15:10
  • @hopeless - that would be Multiple Items. I tend to lean towards Option 2, but by looking at the rails serialize option, I feel like using Hash as an option, option 1 could be accomplished more or less similar to option 1, there by not having another table and more SQL joins. Any feedback is appreciated. Also what exactly you guys mean by cluttering the USERS table, all the preferences will be stored in preferences column only. – Dharam Gollapudi Jul 03 '09 at 16:11
  • @satynos - clutter means the user table is now more confusing because it does more than just (I assume) hold login/name information. If you are serializing, it means more work when you need to look things up, too. Basically you have to parse ALL of the preferences EVERY time you need to know one. Putting the prefs (if there are only a few) as fields on the user table is better than the blob o' prefs because you don't have to look at all the prefs to find out one. – Joel Meador Jul 03 '09 at 17:07
  • thanks for the feedback, so option 2 it is then. – Dharam Gollapudi Jul 04 '09 at 16:37
  • 3
    "Basically you have to parse ALL of the preferences EVERY time you need to know one" -- yeah, and parsing YAML is incredibly fast. FWIW, no one gives any real reasons here, it's pure religion. – Tom Lehman Sep 12 '09 at 04:54
  • @HoraceLoeb I agree: if they're stored as attributes, then every attribute gets parsed every time when the object is loaded from the database. Same issue. – Dogweather Sep 25 '13 at 17:49
  • for rails 4 - http://api.rubyonrails.org/classes/ActiveRecord/Store.html – ajahongir Oct 16 '15 at 19:25

7 Answers7

22

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.

Pesto
  • 23,810
  • 2
  • 71
  • 76
21

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

null
  • 3,959
  • 1
  • 21
  • 28
Turadg
  • 7,471
  • 2
  • 48
  • 49
5

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.

sma
  • 407
  • 4
  • 6
3

Approach 2

You can add preferences, without cluttering up the user table

Rik Heywood
  • 13,816
  • 9
  • 61
  • 81
3

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)
ideasasylum
  • 2,130
  • 1
  • 17
  • 21
1

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

marcgg
  • 65,020
  • 52
  • 178
  • 231
0

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

Jeremy Lynch
  • 6,780
  • 3
  • 52
  • 63