Configure default ordering on models with UUID primary keys in ActiveRecord

In Rails 6 it's possible to configure the implicit order of the first and last methods, which is handy if you use UUIDs for your primary keys.

You may be using UUIDs as the primary key type for your models in your Rails application. Andy Croll has a great post explaining why you might choose to do this.

If you do then you’re probably aware of one of the downsides, which is that calling ActiveRecord’s first and last finder methods to get the oldest or newest record leads to surprising results. This is because the implementations of first and last rely on the primary key being an auto-incrementing integer, so calling them on a model with UUIDs as primary key without an explicit order scope results in a seemingly random record being returned (in reality it’ll be the record with the numerically-highest or lowest UUIDs, but that’s unlikely to be of any help to anyone).

Whilst always being explicit and including an order scope when calling first and last is a good habit to develop, having a sensible default behaviour would also be nice. Thankfully that’ss possible in Rails 6 where you can now configure the implicit order used by the first and last finder methods:

  class Post < ActiveRecord::Base
    self.implicit_order_column = "created_at"

For more details here’s the pull request for this change.

Why not use a default scope?

You might be tempted to solve this issue using a default scope instead, something like this:

  class Post < ActiveRecord::Base
    default_scope { order(:created_at) }

Please don’t. Default scopes can lead to all sorts of strange bugs and confusing behaviour. For example, with the above default scope on the model you’ll find calling Post.order(:name) will behave in a surprising way: the records will be returned ordered by created_at and then name, rather than just by name.

A caveat

If you configure the implicit order to a non-unique column such as the created_at timestamp, there’s a caveat you should keep in mind: if you have two records that end up with identical timestamp then you have no guarantee that the database will return the same record every time you call the finder method. This should be relatively unlikely given Rails and PostgreSQL stores DateTimes with microsecond precision, but keep it in mind all the same.

Get more fab content like this straight to your inbox

You'll get an email whenever I have a fresh insight or tip to share. Zero spam, and you can unsubscribe whenever you like with a single click.

More articles on Ruby & Rails

Authored by Published by