Rails 5 验证失败:志愿者默认事件必须存在....但是为啥会这样以及如何做到这一点

Posted

技术标签:

【中文标题】Rails 5 验证失败:志愿者默认事件必须存在....但是为啥会这样以及如何做到这一点【英文标题】:Rails 5 Validation failed: Volunteer default event must exist....but why this and how to make it soRails 5 验证失败:志愿者默认事件必须存在....但是为什么会这样以及如何做到这一点 【发布时间】:2021-12-25 01:32:54 【问题描述】:

我正在将一个旧应用重新构建到 Rails 5 中。

所以我收到一个错误,我不知道为什么。我不完全确定它要求什么。这是要求修改模型关联吗?我在下面包含了我的模型关联,如果我需要包含其他任何内容来帮助破译此错误,请告诉我。

  binding.pry

    vs.set_values_if_stuck
    vs.assignments = []
    @success = a.valid? && vs.save
    # [1] pry(#<VolunteerEventsController>)> vs.set_values_if_stuck
    #   Roster Load (0.3ms)  SELECT  "rosters".* FROM "rosters" WHERE "rosters"."id" = $1 LIMIT $2  [["id", 7], ["LIMIT", 1]]
    # VolunteerEvent Load (0.3ms)  SELECT  "volunteer_events".* FROM "volunteer_events" WHERE "volunteer_events"."description" = $1 AND "volunteer_events"."date" = $2 LIMIT $3  [["description", "Roster #7"], ["date", "2021-01-11"], ["LIMIT", 1]]
    # (0.1ms)  BEGIN
    # (0.1ms)  ROLLBACK
    # (0.1ms)  BEGIN
    # (0.1ms)  ROLLBACK
    # ActiveRecord::RecordInvalid: Validation failed: Volunteer default event must exist

模型关联

class Assignment < ApplicationRecord
  belongs_to :volunteer_shift
  has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type
  belongs_to :contact ,optional: true
  validates_presence_of :volunteer_shift #belongs_to takes care of this now
  validates_associated :volunteer_shift
  belongs_to :attendance_type
  belongs_to :call_status_type
  validates_presence_of :set_date, :if => :volshift_stuck #belongs_to takes care of this now??

  delegate :set_date, :set_date=, :to => :volunteer_shift
  delegate :set_description, :set_description=, :to => :volunteer_shift

  has_one :contact_volunteer_task_type_count, lambda||
    :conditions => 'contact_volunteer_task_type_counts.contact_id = #defined?(attributes) ? contact_id : "assignments.contact_id"', :through => :volunteer_shift, :source => :contact_volunteer_task_type_counts
  
  scope :date_range, lambda  |range|
    joins(volunteer_shift: :volunteer_event)
        .where(volunteer_shifts:  volunteer_events:  date: range  )
  
  scope :is_after_today, lambda ||
     :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) > ?', Date.today] 
  
  scope :on_or_after_today, lambda ||
     :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) >= ?', Date.today] 
  
  scope :not_cancelled, ->  where('(attendance_type_id IS NULL OR attendance_type_id NOT IN (SELECT id FROM attendance_types WHERE cancelled = \'t\'))')
  scope :roster_is_limited_by_program, -> where("roster_id IN (SELECT id FROM rosters WHERE limit_shift_signup_by_program = 't')").joins(:volunteer_shift)
...
end
class Roster < ApplicationRecord
  has_and_belongs_to_many :skeds
  belongs_to :contact_type
  scope :enabled, -> where(['enabled = ?', true])
  has_many :volunteer_shifts
  has_many :volunteer_default_shifts
  belongs_to :restrict_from_sked, :class_name => "Sked"

  def sandbox?
    name.downcase == 'sandbox'
  end

  def Roster.auto_generate_all
    fails = []
    if Roster.are_auto_generated?
      Roster.enabled.reject(&:sandbox?).each do |r|
        start_d = r.generated_to_date + 1
        end_d = Date.today + eval(Default["autogenerate_volskedj_out"])
        fails = fails + r.auto_generate(start_d, end_d) if start_d <= end_d
      end
    end
    return fails
  end

  def generated_to?(d)
    generated_to_date >= d
  end

  def to_s
    name
  end

  def generated_to_date
    newc = Conditions.new
    newc.apply_conditions()
    newc.roster_enabled = true
    newc.roster_id = self.id
    newc.generated_shift_enabled = true
    d1 = VolunteerEvent.maximum(:date, :conditions => newc.conditions(Assignment), :joins => 'INNER JOIN "volunteer_shifts" ON volunteer_shifts.volunteer_event_id = volunteer_events.id INNER JOIN "assignments" ON assignments.volunteer_shift_id = volunteer_shifts.id LEFT OUTER JOIN "attendance_types" ON "attendance_types".id = "assignments".attendance_type_id')
    d2 = VolunteerEvent.maximum(:date, :conditions => newc.conditions(ResourcesVolunteerEvent), :joins => 'INNER JOIN "resources_volunteer_events" ON resources_volunteer_events.volunteer_event_id = volunteer_events.id')
    return [d1, d2, Date.today].select|x| !!x.max
  end

  def auto_generate(from, to)
    results = []
    begin
      c = Conditions.new
      c.apply_conditions()
      c.roster_enabled = true
      c.roster_id = self.id
      conflicts = VolunteerDefaultShift.find_conflicting_assignments(from, to, c)
      skippers = conflicts.map|x| x[1].id
      results = conflicts.map|x| "On #x[0], #x[1].contact.display_name (##x[1].contact_id) was not successfully scheduled for #x[1].slot_type_desc (#self.name roster) as they have the following conflicting shifts: #x[2].map|x| x.description.join(" ")"
      VolunteerDefaultShift.generate(from, to, c, skippers)
      ResourcesVolunteerDefaultEvent.generate(from, to, c)
    rescue => e
      puts "ERROR: Failed to generate #self.name roster from #from to #to!"
      puts "Please check for consistency,"
      puts "  error message: #e.to_s"
    end
    results
  end

  def Roster.are_auto_generated?
    ! Default["autogenerate_volskedj_out"].nil?
  end

  def skeds_s
    self.skeds.map(&:name).sort.join(", ")
  end

  # note: #Keep this here so know where to find the deprecation info - Fonso
  # THIS is where date value from add new record form gets inserted into a new record
  def vol_event_for_date(date)
    binding.pry
    # VolunteerEvent.find_or_create_by_description_and_date("Roster ##self.id", date) <-- original
    # https://guides.rubyonrails.org/4_0_release_notes.html#active-record-deprecations
    VolunteerEvent.find_or_create_by(description: "Roster ##self.id", date: date)
    binding.pry
  end

  def vol_event_for_weekday(wday)
    # VolunteerDefaultEvent.find_or_create_by_description_and_weekday_id("Roster ##self.id", wday) <-- original
    # https://guides.rubyonrails.org/4_0_release_notes.html#active-record-deprecations
    VolunteerDefaultEvent.find_or_create_by(description: "Roster ##self.id", weekday_id: wday)
  end
end

class VolunteerEvent < ApplicationRecord
  belongs_to :volunteer_default_event
  validates_presence_of :date
  has_many :volunteer_shifts, :dependent => :destroy
  has_many :resources_volunteer_events, :dependent => :destroy
  validates_associated :volunteer_shifts
...
end
class VolunteerShift < ApplicationRecord
  validates_presence_of :roster_id
  validates_presence_of :end_time
  validates_presence_of :start_time
  has_many  :assignments
  belongs_to :volunteer_default_shift
  belongs_to :volunteer_task_type
  belongs_to :roster
  belongs_to :volunteer_event
  belongs_to :program
  has_many :contact_volunteer_task_type_counts, :primary_key => 'volunteer_task_type_id', :foreign_key => 'volunteer_task_type_id' #:through => :volunteer_task_type

...
end

感谢您的宝贵时间

【问题讨论】:

【参考方案1】:

在你的VolunteerEvent 中有这条线

belongs_to :volunteer_default_event

自从 Ruby on Rails 5.0 以来,belongs_to 关联的默认值是它们必须存在才能使记录有效(请参阅article about this behavior)。这基本上意味着每次您定义像belongs_to :foo 这样的关联时,都会自动添加一个validates_presence_of :foo

你必须有办法解决这个问题:

    您确保将volunteer_default_event 设置为有效事件。或者

    您将关联标记为可选,这意味着没有volunteer_default_event 时可以。可以这样做:

    belongs_to :volunteer_default_event, optional: true
    

【讨论】:

以上是关于Rails 5 验证失败:志愿者默认事件必须存在....但是为啥会这样以及如何做到这一点的主要内容,如果未能解决你的问题,请参考以下文章

Rails 中的 Authorize.net - 由于身份验证值无效,用户身份验证失败

用户必须存在于Rails 5中

Rails 5 和设计:如何在不更改默认策略的情况下禁用基于令牌的策略上的会话

在 Rails 中显示/隐藏部分/模板的更干燥的方式

rails 关联:用户必须存在问题

“父必须存在”和“没有为CommentsController找到模板#create,呈现head:no_content”Rails 5