In Django, the ORM has a function called get()
which is used to return only a single instance of a model. Specifically, it raises an exception if multiple objects are returned when you only expected one, so it's great for certain lookups (ie "Find the one article with this slug").
I haven't been able to find an analogous function in Rails/ActiveRecord that has the same behavior. So far, I've just been writing code like:
Model.where( ... ).first
But this has lead to silent bugs where multiple instances of an object were returned -- which is really a bad, ambiguous situation -- and we just grabbed the first one and carried on like everything was okay.
The Active Record Query Interface guide lists 5 ways of retrieving a single object, with find()
and find_by()
looking the most promising, but neither of them raises an exception when multiple objects match the search criteria.
I know that I could just write where()
and find_by()
queries and check the number of objects returned myself, but this code appears everywhere in our codebase and I'd rather not add all that cruft. It also seems like such a generic, common need that I'd expect this to be baked into Rails/ActiveRecord somewhere.
Is there some function I'm missing that'd make it easier to catch these situations? We're using Rails 4 and Ruby 2.0 if it helps.
Rails 7.0 introduces find_sole_by
(or where(...).sole
) for this.
Seems to fit the bill perfectly:
Finds the sole matching record. Raises
ActiveRecord::RecordNotFound
if no record is found. RaisesActiveRecord::SoleRecordExceeded
if more than one record is found.
Use case is simple:
Model.find_sole_by(conditions: 'here')
I'm not aware of a built in Active Record method that does what you ask but it wouldn't be hard to write your own. Something like:
class YourModel
def self.find_only_one_by_slug(slug)
results = YourModel.where(slug: slug)
if results.size > 1
raise "There should only be one return value."
else
return results.first
end
end
end
I haven't tested the above code so there will most certainly be errors but you get the idea. You could even take this a step farther and add a similar method to active record base that all of your models would have access to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With