【问题标题】:Rails validating that a Has Many relationship has at least 1 entryRails 验证 Has Many 关系至少有 1 个条目
【发布时间】:2015-10-24 23:00:38
【问题描述】:

我有一个事件模型。每个事件可以有多个会话。

我想确保没有至少 1 个会话与之关联的模型不能存在。

  validates :sessions, :length => { :minimum => 1 }

问题是 - 当我尝试通过调用我的模型方法为特定事件创建会话时:

create_sessions()

类似于:

sessions.create(event_id: id,date: x,day_of_the_week:x.strftime("%A"),classPin: pin)

活动将在每个日期举行。

保存失败,报错:

ActiveRecord::RecordNotSaved in EventsController#create
You cannot call create unless the parent is saved

当然 - 到目前为止,新的事件记录尚未保存 - 因此无法创建此关联,因为在保存父级之前无法使用的关联上创建!

因此,这种关系之间的任何验证如何工作 - 因为验证发生在保存时间......但我想在我们保存事件之前验证会话数是否大于 0!

【问题讨论】:

    标签: ruby-on-rails ruby validation ruby-on-rails-4 activerecord


    【解决方案1】:

    我认为更简单的方法是使用new 而不是create 创建您的事件和会话,它只会创建对象但不会将它们存储到数据库中。在您创建会话后,请在您的活动中使用save

    @event = event.new(event_args)
    @event.sessions.new(session_args)
    @event.save
    

    加法
    @event.save 将存储 @event 对象和所有关联的 session 对象。如果没有进一步的配置,只有 新的 个对象会受到保护,请参阅Active Record Autosave Association 了解更多信息。

    【讨论】:

    • 在事件保存期间是否也会进行会话验证?
    • 默认情况下不会在save 上验证会话,您需要添加validates_associated :sessions 参见Rails Validation Guide
    【解决方案2】:

    重点是您的验证与 ActiveRecord 的工作方式相冲突。你正在创造一个悖论。

    有多种解决方案,但您必须放松验证。也就是说,它不应该在新记录时运行。

    你可以这样定义它:

    class Event < ActiveRecord::Base
      validates :sessions, length: { minimum: 1 },
                           unless: :new_record?
    end
    

    或者,为了获得更大的灵活性,您可以使用自定义验证:

    class Event < ActiveRecord::Base
      validate :session_count_validation
    
      private
    
      def session_count_validation
        if !new_record? && sessions.count < 1
          errors.add(:base, "Not enough sessions!")
        end
      end
    end
    

    第二种样式还具有更高性能的优点:sessions.count 将简单地计算存在多少关联的Session 记录,而您的原始验证将将所有这些记录加载到内存中,并且检查关系/数组的长度。

    然后,为确保您的创建逻辑正确,您应该使用transaction

    begin
      ActiveRecord::Base.transaction do
        event = Event.create!(args)
    
        sessions = dates.map do |date|
          event.sessions.build(event_id: id,
                                   date: date,
                        day_of_the_week: date.strftime("%A"),
                               classPin: pin)
        end
    
        sessions.each(&:save!)
      end
    rescue
      # your rescue logic
      # e.g. display an error to the User
    end
    

    这样,如果任何save! 操作失败,一切都会安全回滚。

    【讨论】:

      猜你喜欢
      • 2011-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多