在表单嵌套模型中销毁记录而不是更新
Destroy record instead of update in form nested model
我有一个模型 opening_times
可以记录商店的营业时间。
create_table "opening_times", force: :cascade do |t|
t.string "day"
t.time "morning"
t.time "evening"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "shop_id"
t.index ["shop_id"]
end
此模型仅通过 shops
控制器的 edit
操作中的嵌套形式更新。我没有 opening_times
控制器。
那么基本上我能做的就很有限了:update
动作。
虽然我有一个问题:当过去有特定一天的开放时间时,比如说星期二,并且用户想让那一天不工作,用户让两个字段 morning
和 evening
空白。
我可以在数据库中保存空值,但实际删除星期二的记录会更好。
然后在模型文件中我设置了这个:
before_save :delete_records_with_missing_hours
private
def delete_records_with_missing_hours
if self.morning.blank? or self.evening.blank?
self.destroy
end
end
但是没用。
有没有办法删除打算在模型级别更新的记录?
我认为您最好为此操作创建某种维护脚本。比如抽佣任务之类的。因此,您定期 运行 一个简单的查找和销毁:
OpeningTime.where(morning:nil, evening:nil).destroy_all
只需每天或每周运行清理您的数据库,不要在保存时为记录操心。
我认为您当前代码的问题在于您正在删除 before_save 中的记录,因此这会产生两个问题:
1/ 如果这不是一个持久记录,而是一个新记录,rails 应该怎么办? Destroy 会失败,整个事务都会回滚(看看你的控制台)
2/ 即使记录被持久化,删除它也可以,但是接下来执行的保存操作会失败,也会导致回滚。
有一种 Rails 方法来处理这个问题。这可以很容易地完成,但您需要从您的 Shop 模型中完成。在该模型中插入:
class Shop < ApplicationRecord
accepts_nested_attributes_for :opening_times, allow_destroy: true, reject_if: :reject_opening_time?
def reject_opening_time?(attributes)
persisted = attributes[:id].present?
time_values = attributes.slice(:morning, :evening).values
without_time = time_values.any?(&:blank?)
attributes.merge!(_destroy: true) if persisted and without_time
without_time && !persisted # Return false so as to reject new opening_time if any time attributes are empty
end
end
现在,对于每个 opening_time
嵌套记录,Rails 将评估时间属性。如果任何时间值为空,它将适当地处理记录。如果记录被持久化,它会添加一个 _destroy
属性,这将在您保存父级时销毁嵌套记录。如果记录没有持久化,当你保存父项时,它会被拒绝(忽略)。
如果您不想让 morning
和 evening
的字段为空值,为什么不验证这些字段的存在呢?它会比使用外部方法来验证这种情况更清晰。
首先,你应该把它放在 Shop
模型中:
validates_associated :opening_times
此代码将确保您的关联在 inserted/updated
之前得到验证
现在您可以在 OpeningTime
模型中进行验证,如下所示:
validates :morning, :evening, presence: true
如果你只想在更新操作中这样做,你甚至可以这样做:
validates :morning, :evening, presence: true, on: :update
如果您希望至少出现一个值,您还可以使用:
validate :morning_and_evening_validation
def morning_and_evening_validation
morning.present? || evening.present?
end
我认为主要是它更加清晰易读。
我有一个模型 opening_times
可以记录商店的营业时间。
create_table "opening_times", force: :cascade do |t|
t.string "day"
t.time "morning"
t.time "evening"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "shop_id"
t.index ["shop_id"]
end
此模型仅通过 shops
控制器的 edit
操作中的嵌套形式更新。我没有 opening_times
控制器。
那么基本上我能做的就很有限了:update
动作。
虽然我有一个问题:当过去有特定一天的开放时间时,比如说星期二,并且用户想让那一天不工作,用户让两个字段 morning
和 evening
空白。
我可以在数据库中保存空值,但实际删除星期二的记录会更好。
然后在模型文件中我设置了这个:
before_save :delete_records_with_missing_hours
private
def delete_records_with_missing_hours
if self.morning.blank? or self.evening.blank?
self.destroy
end
end
但是没用。
有没有办法删除打算在模型级别更新的记录?
我认为您最好为此操作创建某种维护脚本。比如抽佣任务之类的。因此,您定期 运行 一个简单的查找和销毁:
OpeningTime.where(morning:nil, evening:nil).destroy_all
只需每天或每周运行清理您的数据库,不要在保存时为记录操心。
我认为您当前代码的问题在于您正在删除 before_save 中的记录,因此这会产生两个问题:
1/ 如果这不是一个持久记录,而是一个新记录,rails 应该怎么办? Destroy 会失败,整个事务都会回滚(看看你的控制台)
2/ 即使记录被持久化,删除它也可以,但是接下来执行的保存操作会失败,也会导致回滚。
有一种 Rails 方法来处理这个问题。这可以很容易地完成,但您需要从您的 Shop 模型中完成。在该模型中插入:
class Shop < ApplicationRecord
accepts_nested_attributes_for :opening_times, allow_destroy: true, reject_if: :reject_opening_time?
def reject_opening_time?(attributes)
persisted = attributes[:id].present?
time_values = attributes.slice(:morning, :evening).values
without_time = time_values.any?(&:blank?)
attributes.merge!(_destroy: true) if persisted and without_time
without_time && !persisted # Return false so as to reject new opening_time if any time attributes are empty
end
end
现在,对于每个 opening_time
嵌套记录,Rails 将评估时间属性。如果任何时间值为空,它将适当地处理记录。如果记录被持久化,它会添加一个 _destroy
属性,这将在您保存父级时销毁嵌套记录。如果记录没有持久化,当你保存父项时,它会被拒绝(忽略)。
如果您不想让 morning
和 evening
的字段为空值,为什么不验证这些字段的存在呢?它会比使用外部方法来验证这种情况更清晰。
首先,你应该把它放在 Shop
模型中:
validates_associated :opening_times
此代码将确保您的关联在 inserted/updated
之前得到验证现在您可以在 OpeningTime
模型中进行验证,如下所示:
validates :morning, :evening, presence: true
如果你只想在更新操作中这样做,你甚至可以这样做:
validates :morning, :evening, presence: true, on: :update
如果您希望至少出现一个值,您还可以使用:
validate :morning_and_evening_validation
def morning_and_evening_validation
morning.present? || evening.present?
end
我认为主要是它更加清晰易读。