Proposal + patch: FinderMethods#only! for asserting there's only one result row

Django ORM’s QuerySet has a get() method, which asserts that the query only matches a single row. For some reason I thought find_by did that in Rails, too, but recent poking around led me to realise that’s not the case and I might’ve relied upon this somewhere :sweat_smile:

I put together a diff that adds the method and some entry-level tests: https://github.com/rails/rails/compare/master...kivikakk:only

The basic usage looks like this (adapted from an actual use case):

Product.where(price: price, annual: true).only!

Would the core team be interested in accepting a PR for this?

It fills the niche where, for whatever reason, adding a validation or constraint that covers the exact circumstances the query is expressing is clunky or untenable, and helps express intention at the point of use that there should be only one matching row.

2 Likes

The preferred way to fetch a single record with find_by is

Product.find_by(price: price, annual: true).first

or

Product.find_by(price: price, annual: true).first!

if you want Rails so raise an ActiveRecord::RecordNotFound error.

It seems like your .only method is just duplicating this behavior. I just wanted to mention that in case you didn’t know. But don’t let this discourage you from issuing a pull request, I am not writing on behalf of the core team, of course.

All the best,

Michael

This seems to be a different use case than fetching a single record. The @kivikakk is proposing a finder method which returns the first and only record satisfying the query conditions, and raises an error in the cases where there is no matching record or (more interestingly) multiple matching records.

This seems like it would be useful, IMO. In fact, I can think of several places in our codebase at work where this might prove useful.

I think there might be some confusion; find_by already returns one record or nil. Your examples would make more sense as where(…).first, which is essentially what find_by does.

Per @Anthony_Hernandez’s comment, I’d like to assert that there is exactly one record, whereas find_by doesn’t care if there are multiple matches.

Alright. Besides me messing up .where().first and .find_by().first (sorry for that) I did not get the critical point.

I’d agree, this sounds useful.

My hopes for https://bugs.ruby-lang.org/issues/13683 fluctuate over time.

I do rather like your choice of name :slightly_smiling_face: – wow, that was a while ago. I’ve since realised we already have a Relation#only, with a different meaning, which doesn’t seem worth moving unless upstream were to adopt that method name.

2 Likes

I have had this exact feature monkey-patched as one! method.