Should I create a model if it will only have 1 row?

A post on Reddit says:

I’m not sure how best to do this. I would just set up a model, but that seems wrong to have a model with just 1 row in it (it’s a single charity). Is there a better way to do this? STI? something else?

It can seem like overkill to create a database table and a corresponding ActiveRecord model if you know for a fact that your table will only have one row. This is a pretty common situation, and there are a couple of ways to deal with this.

Note: Pat Maddox’s amazing comment on Reddit, which addresses this question, was the primary inspiration for this post.

First, if non-developers need the ability to update information that this “model” would contain, then yes, you should create an ActiveRecord model. This will of course give you all the benefits of ActiveRecord as well, like being able to use has_many, belongs_to and everything else.

If only developers are going to be concerned with and using this model, you don’t have to tie it to the database and can implement it as a “Plain Old Ruby Object” aka a “PORO”.

The advantage with PORO’s is that they are cheap to instantiate because you won’t be making a call to the database when you do so. Setting up association-like behavior can be as simple as defining either instance or class methods on the PORO. As Pat advises in his comments however, you should always ask yourself what benefit you’d get from implementing association-like behavior in your PORO to ensure that you’re doing it for the right reasons, and to keep your code easy to understand.

So, going with the example in the Reddit post above, where we have a Charity model (this is the model that will only have one “row” and the one we will be writing a PORO for) which needs to define a has_many type association with the Donation model, you’d have something that looks like:

class Charity #this is our PORO
  def name
    'The Human Fund'
  end

  def donations
    Donation.all
  end
end

class Donation < ActiveRecord::Base #this corresponds to a table in the DB
  validates :amount, presence: true

  def charity
    Charity.new
  end
end

As you can see, we’re manually defining the methods (#donations and #charity) that we’d get automatically if we used has_many. This allows us to get around the fact that a Charity object does not correspond to a table in the database, and lets other parts of our code use Charity objects as they would any other ActiveRecord object.

For more association type goodness on POROs, also check out the activemodel-associations gem.

So if you decide to go with a PORO, you next question is probably: Where should this file reside?

There are a couple of common options – in the models directory with the rest of the models in /app/models, or in the /lib directory. Pat Maddox recommends the models directory, and I concur, because if you’re going to treat it like a model in other parts of your code, it’s natural that other developers (or you in the future) will look for the class in the models directory. That being said, this is just a convention, and either way works.

As always, let me know in the comments below if you have a question that is not covered by this article (maybe polymorphic associations?), and I’ll get back to you ASAP.

2 thoughts on “Should I create a model if it will only have 1 row?

  1. I would say create the model because unless you are absolutely sure it will ALWAYS just contain one row, the cost of a later refactor is greater than the simplicity of simply having the model.

    1. Thanks for your comment Brian. I think it depends on how you structure it, but it could go either way.

      Sometimes (as in the example on Reddit), we can be sure that we just need a PORO to hold some static data. And if you stick to ActiveRecord conventions, I imagine a shift from PORO to ActiveModel will involve more code deletion than addition (though you might want to add tests to check DB persistence).

      The main advantage I see with the PORO is flexibility. You can change ‘column’ names, return types, defaults etc all without creating a migration. Also if you have a model with only row and you set it up to have associations, you might need additional migrations to add foreign keys and such.

Leave a Reply

Your email address will not be published.