【问题标题】:Associated model validations in Rails - and some questions of designRails 中的相关模型验证 - 以及一些设计问题
【发布时间】:2011-01-03 21:54:38
【问题描述】:

我有一个包含许多 LoggedTimes 的任务。我想在任何一天对任务设置 8 小时的记录时间限制。解决此问题的最佳方法是什么,以便我可以检查此人最近记录的时间是否“超过了一天的总时间”,可以这么说?

这是我开始的地方(Task.rb):

validate :max_logged_daily_time

def max_logged_daily_time
    if (params[:session_time] + (logged_times.where(:created_at => Date.today).to_a.sum(&:session_time)/60)) > 8
      errors.add_to_base("Can't have more than 8 hours logged a day") 
      logged_time.errors.add('session_time', 'Logged times exceeded today')
    end
  end

目前此验证不起作用(在注册了 8 小时以前的记录时间后添加另一个 LoggedTime 只需将其添加到其余时间,而不是引发错误。由于没有引发错误,我正在努力选择我的解决问题的方法。是不是和params的处理有关?

这让我想到了设计问题:理论上我可以修改视图,使用户只能提交 8 小时减去他们当天登录的总时间;然而,这似乎是一个笨拙的解决方案,并且违反了在模型中保留验证的原则。 (而且它当然不能帮助我解决这个模型验证问题)。

这里有什么建议吗?

TIA

【问题讨论】:

    标签: ruby-on-rails validation associations ruby-on-rails-3


    【解决方案1】:
    class Task < ActiveRecord::Base
      has_many :logged_times
    
      def hours_today
        LoggedTime.daily_hours_by_task(self).to_a.sum(&:session_time)
      end
    
    end
    
    class LoggedTime < ActiveRecord::Base
      belongs_to :task
    
      scope :daily_hours_by_task, lambda { |task| task.\
        logged_times.\
        where('logged_times.created_at >= ? AND logged_times.created_at < ?',
              Date.today, Date.today + 1) }
    
      validate :max_logged_daily_time
    
      private
    
      def max_logged_daily_time
        if task && ((task.hours_today + session_time) / 60.0) > 8
          errors.add('session_time', 'Logged times exceeded today')
        end
      end
    
    end
    

    一些注意事项:

    • created_at 是一个日期时间,所以你会 需要测试开始和结束 当天的

    • 验证还可以防止 添加一个单独超过最大值的 LoggedTime。

    • 除以整数会截断和 会给出错误的结果——添加 .0 转换为浮点数。

    • 这仅验证 LoggedTime, 不是任务,所以你可能想要 添加validates_associated 在 任务模型。

    • Task 为 nil 时绕过验证

    编辑

    嗯,hours_today 确实应该被称为 minutes_today,但你明白了。

    【讨论】:

    • 感谢您的建议。这里的一些概念对我来说是较新的(菜鸟),所以我必须挑选它们并消化它们,看看它们是否有效:) 干杯
    • 你好,zetetic。我可以问为什么我不会通过任务模型进行验证,并绕过 validates_associated 吗?换句话说,与在 Task 模型中进行验证相比,您在 LoggedTime 上的范围方法的优点是什么? def total_hrs_for_date(date = Date.today) practice.where('created_at >= ? AND created_at
    • 您可以在 cmets 中使用反引号(`)来表示等宽字体,但这就是您获得的所有格式。
    • Re: 在哪里放置验证,IMO 它在 LoggedTime 中工作得更好,因为这样你可以在没有任何时间的情况下添加任务。这是我的一个假设——也许你会看到不同的情况。您可以尝试将其移动到任务中,看看它是如何工作的。顺便说一句,在belongs_to 端写范围更容易,所以我把它放在那里。
    【解决方案2】:

    我将创建一个单独的方法来获取给定一天的小时总和。

    def total_hrs_logged_for_date(date)
      #some code
    end
    

    测试该方法以确保其有效。

    可能也对计算当前记录的时间执行相同的操作。

    然后在您的自定义验证器中使用这两个

    所以这一行

    if (params[:session_time] + (logged_times.where(:created_at => Date.today).to_a.sum(&:session_time)/60)) > 8
    

    变成

    if total_hrs_logged_for_date(Date.today) + current_time_being_logged > 8
    

    至少它会帮助您缩小其中哪些不起作用。

    我还注意到您有“params[:session_time]”

    我认为这是在 Task.rb 中,听起来像一个模型。可能你想要的只是“session_time”。

    【讨论】:

    • 感谢您的建议。只是想知道我是否能够调用 session_time,因为这是 LoggedTime 上的一个方法......因为它尚未保存(因为它尚未验证),我将如何从视图中访问该数据?
    【解决方案3】:

    我最终稍微修改了 Zetetic 的回复,因为我无法让它按原样工作。

    最后,这成功了:

    class Task < ActiveRecord::Base
      has_many :logged_times
      validates_associated :logged_times
    
      def minutes_today
        logged_times.where('created_at >= ? AND created_at < ?', Date.today, Date.today + 1)
      end
    
    end
    

    以及 LoggedTime 模型:

    class LoggedTime < ActiveRecord::Base
      belongs_to :task
    
      validate :max_logged_daily_time
    
      private
    
      def max_logged_daily_time
        if task && ((task.minutes_today + session_time) / 60.0) > 8
          errors.add('session_time', 'Logged times exceeded today')
        end
      end
    
    end
    

    我不确定为什么 scope 方法会失败,但确实如此。 Zetetic有什么提示吗?

    【讨论】:

      猜你喜欢
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2015-12-28
      • 2023-03-18
      • 2011-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多