If you know something should return a single row, then you might as
well check check if it's really the case.
Also, first has a different meaning than one/unique.
If I say that I want the first of a query, there could be more results
in the resultset.
The 'first user' example is a good example of a case where you'd want
to use first. You could also use last.
But the second query, if username should be unique, User.one or
User.unique better states what you're trying to do. You could also
write User.last(.....), but that would look awkward.
If I would mistakenly write the query this way:
User.first(:conditions => :firstname => 'mislav'), I wouldn't be
notified if the query returned 3 mislav users.
I have to agree with Mislav and Duncan here, if firstname should be unique, a finder is not the place to enforce that. Those 2 other "mislav" users should not be in the database in the first place! There may be a compelling use-case for a find(:only/unique/make_sure_there_is_only_one_result), but late detection of invalid data isn't it.
In a world where developers never make assumptions, I'd agree with you
entirely. However we do, and we frequently make them incorrectly.
If you believe the column (or other condition) is unique, and want to
fail-fast if you're incorrect, then you *do* need this. Take the
following:
Status.find_by_code("cancelled")
If I build this right, and put a unique index and a NOT NULL on the
code column, you're right there's no difference. However if I leave
off that unique index because I forgot it or mis-merged a migration
then my code will be 'randomly' returning one of the two cancelled
statuses and slowly messing up my data leaving a huge clean up job.
This to me is akin to foo.bars.create! where there are *currently* no
validations but you want exceptions rather than silent failures if you
add one later on.
--
Cheers
Koz
>
>> if the column is unique-constrained, you should use find_by_column
>> (or
>> `first` with conditions or scope);
>> if the column is not unique, you must use find_all_by_column (or
>> alternatives);
>> there's no need for an extra method.
>
> In a world where developers never make assumptions, I'd agree with you
> entirely. However we do, and we frequently make them incorrectly.
>
> If you believe the column (or other condition) is unique, and want to
> fail-fast if you're incorrect, then you *do* need this.
If you believe this, you should have database setup and tests checking
for this, not testing-related code in production code slowing
everything down. The test should catch whatever you fail to do at the
database level.
Uniquenes should be maintained in the database, because
a) we have a cheap and quick mechanism which fails quickly (unique
index)
b) it is insanely expensive to guarantee database record uniqueness in
application code and most people who try are doing it wrong
What you are saying is that adding :unique is just replacement for not
having tests. Encouragement for not writing tests probably doesn't
belong in the core :-)
There may exist some validations which are record-local or so complex
that application code is the right approach, but database integrity
should really be done in database if at all possible...
izidor
You could make a similar case regarding escaping HTML output, but I
think that's a useful feature for rails to have. I think the same
about a :unique or similarly named feature, I would find it useful and
would use it on User.find_by_name and similar calls. Yes, I have
unique constraints in my database and use validates_uniqueness_of, but
I find it useful in these cases to practice defensive programming.
- donald
> It's a very slippery slope if the core starts trying to predict and
> protect developers from non-Rails assumptions.
I agree. It is kind of a finder mixed with an assertion. Doesn't fit
in core in my view. It's easy to write it yourself nonetheless.