1

I have a Rails model that should only allow saving/updating of a model once per day per user. I have a callback to do the Find by user and date then add to errors but this is ugly and feels un-rails-like. I have the typical created_at / updated_at columns (and the time portion is significant/I need to keep it).

So I figure I could either:

1) Create another model attribute which is just the date of creation and scope by that (bleh)

2) Use the :scope attribute but somehow get just the date part of created_at, e.g. validates_uniqueness_of :user, :scope => :created_at.to_date (doesn't work, obviously)

3) Validate unless => Proc.new{ |o| Finder that matches my existing callback } (gross)

http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000086

There will not be an overwhelming number of these, but I'd rather that it is done in SQL instead of Ruby (for obvious scalability reasons).

Any thoughts? Is there a better way?

Matt Rogish
  • 24,435
  • 11
  • 76
  • 92

2 Answers2

6

I ended up using the conditions option with validates_uniqueness_of and passing it lambda that limits the records checked to just those for today.

class AttendanceRecord < ActiveRecord::Base
  validates_uniqueness_of :user_id, conditions: { -> { where("DATE(created_at) = ?", Date.today) } }
end
Patrice
  • 4,641
  • 9
  • 33
  • 43
dustinbrownman
  • 163
  • 1
  • 5
  • dont think you need the extra braces around conditions (but site conditions won't let me edit it either) – dtc Aug 05 '15 at 02:43
  • This works if the date is hard-coded to `Date.today`, but will allow multiple duplicate AttendanceRecords if the `created_at` date (or equivalent attribute for some other model) is set to something other than today. – Richie Thomas Apr 16 '19 at 04:52
  • 1
    Remember to also add a unique index to enforce this rule on the database level. This is mentioned in the Rails doc for uniqueness validations: https://guides.rubyonrails.org/active_record_validations.html#uniqueness – shime Sep 10 '19 at 11:28
1

You can also write your own validates method. This is quite easy. And in the custom validate method, date_trunc (Postgresql) can be used to find existing records in required time span. date_trunc can be also parametrized (e.g.hour, day, week, month).

Instead of date_trunc a simple condition can be used to find conflicting record. e.g. ["user_id = ? AND updated_at >= ?", self.user_id, Time.now.beginning_of_day] And I guess this record look up should be faster because it can use an index.

Greg Dan
  • 6,198
  • 3
  • 33
  • 53