Ruby on rails Rails 5 API-控制器更新操作有时不反映数据库更改(缓存问题)?
我有两个简单的模型,有很多关系。一个模板有许多模板项。模板的模板类型可以是两个值(“模板”或“检查表”)中的一个 为了简洁起见,我删除了不相关的代码 模板.rbRuby on rails Rails 5 API-控制器更新操作有时不反映数据库更改(缓存问题)?,ruby-on-rails,ruby,caching,controller,Ruby On Rails,Ruby,Caching,Controller,我有两个简单的模型,有很多关系。一个模板有许多模板项。模板的模板类型可以是两个值(“模板”或“检查表”)中的一个 为了简洁起见,我删除了不相关的代码 模板.rb class Template < ApplicationRecord # Relationships belongs_to :account has_many :template_items, -> { order('sort ASC') }, dependent: :destroy accepts_nes
class Template < ApplicationRecord
# Relationships
belongs_to :account
has_many :template_items, -> { order('sort ASC') }, dependent: :destroy
accepts_nested_attributes_for :template_items, allow_destroy: true
# Enums
enum template_type: {template: 0, checklist: 1}
enum status: {not_started: 0, started: 1, completed: 2}
# Callbacks
before_save :set_status, unless: :is_template? # only care about status for checklists
def is_template?
return self.template_type == 'template'
end
def set_status
completed = 0
self.template_items.each do |item|
completed += 1 if item.is_completed
end
case completed
when 0
self.status = Template.statuses[:not_started]
when 1..(self.template_items.length - 1)
self.status = Template.statuses[:started]
when self.template_items.length
self.status = Template.statuses[:completed]
end
end
end
class TemplateItem < ApplicationRecord
# Relationships
belongs_to :template
# Validations
validates_presence_of :template
end
def template_params
params.require(:template).
permit(:id, :account_id, :list_type, :name, :title, :info, :status,
template_items_attributes:
[:id, :template_id, :is_completed, :content, :item_type, :sort, :_destroy])
end
def update
if @template.update(template_params)
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
def update
if @template.update(template_params)
@template.template_items.reload
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
class TemplateSerializer < ActiveModel::Serializer
attributes :id,
:account_id,
:name,
:info,
:title,
:template_type,
:status
has_many :template_items, include_nested_associations: true
end
请注意,项的一个属性称为sort。还要注意,排序顺序在模板
模型中用于对模板项
进行排序(请参见has\u many
行)
如果客户机使用模板项,则调用以下更新操作:
模板\u controller.rb
class Template < ApplicationRecord
# Relationships
belongs_to :account
has_many :template_items, -> { order('sort ASC') }, dependent: :destroy
accepts_nested_attributes_for :template_items, allow_destroy: true
# Enums
enum template_type: {template: 0, checklist: 1}
enum status: {not_started: 0, started: 1, completed: 2}
# Callbacks
before_save :set_status, unless: :is_template? # only care about status for checklists
def is_template?
return self.template_type == 'template'
end
def set_status
completed = 0
self.template_items.each do |item|
completed += 1 if item.is_completed
end
case completed
when 0
self.status = Template.statuses[:not_started]
when 1..(self.template_items.length - 1)
self.status = Template.statuses[:started]
when self.template_items.length
self.status = Template.statuses[:completed]
end
end
end
class TemplateItem < ApplicationRecord
# Relationships
belongs_to :template
# Validations
validates_presence_of :template
end
def template_params
params.require(:template).
permit(:id, :account_id, :list_type, :name, :title, :info, :status,
template_items_attributes:
[:id, :template_id, :is_completed, :content, :item_type, :sort, :_destroy])
end
def update
if @template.update(template_params)
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
def update
if @template.update(template_params)
@template.template_items.reload
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
class TemplateSerializer < ActiveModel::Serializer
attributes :id,
:account_id,
:name,
:info,
:title,
:template_type,
:status
has_many :template_items, include_nested_associations: true
end
奇怪的行为是,数据库总是被更新(在日志和数据库中进行验证),但有时渲染不会渲染新的排序顺序,而是渲染以前的排序顺序
以下是操作错误返回以前数据时的日志:
I, [2018-02-20T20:22:55.997835 #1852] INFO -- : Processing by Api::TemplatesController#update as JSON
...parameters here...
D, [2018-02-20T20:22:56.002965 #1852] DEBUG -- : User Load (1.7ms) SELECT "users".* FROM "users" WHERE "users"."uid" = $1 LIMIT $2 [["uid", "rmcsharry+owner@gmail.com"], ["LIMIT", 1]]
D, [2018-02-20T20:22:56.115190 #1852] DEBUG -- : Template Load (2.6ms) SELECT "templates".* FROM "templates" WHERE "templates"."id" = $1 ORDER BY LOWER(templates.name) ASC LIMIT $2 [["id", "f9f6bca2-cb84-4349-8546-ca38026db407"], ["LIMIT", 1]]
D, [2018-02-20T20:22:56.121995 #1852] DEBUG -- : (0.4ms) BEGIN
D, [2018-02-20T20:22:56.129177 #1852] DEBUG -- : TemplateItem Load (2.5ms) SELECT "template_items".* FROM "template_items" WHERE "template_items"."template_id" = $1 AND "template_items"."id" IN ('419cb7ec-ca3f-4911-8a00-bec20f5ca89c', 'a7ac1687-8cb5-4199-a03b-d7cc975a0387', 'd7d885b6-2a75-487a-918c-6f3abaae7df1', 'b1b0277c-632f-4fe1-82e5-d020ee313d5b') ORDER BY sort ASC [["template_id", "f9f6bca2-cb84-4349-8546-ca38026db407"]]
D, [2018-02-20T20:22:56.137975 #1852] DEBUG -- : Account Load (1.4ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2 [["id", "c379e356-4cce-4de2-b1b4-984b773dd43e"], ["LIMIT", 1]]
D, [2018-02-20T20:22:56.144421 #1852] DEBUG -- : CACHE Template Load (0.0ms) SELECT "templates".* FROM "templates" WHERE "templates"."id" = $1 ORDER BY LOWER(templates.name) ASC LIMIT $2 [["id", "f9f6bca2-cb84-4349-8546-ca38026db407"], ["LIMIT", 1]]
D, [2018-02-20T20:22:56.148992 #1852] DEBUG -- : CACHE Template Load (0.0ms) SELECT "templates".* FROM "templates" WHERE "templates"."id" = $1 ORDER BY LOWER(templates.name) ASC LIMIT $2 [["id", "f9f6bca2-cb84-4349-8546-ca38026db407"], ["LIMIT", 1]]
D, [2018-02-20T20:22:56.156300 #1852] DEBUG -- : TemplateItem Load (2.4ms) SELECT "template_items".* FROM "template_items" WHERE "template_items"."template_id" = $1 ORDER BY sort ASC [["template_id", "f9f6bca2-cb84-4349-8546-ca38026db407"]]
D, [2018-02-20T20:22:56.171567 #1852] DEBUG -- : SQL (1.9ms) UPDATE "template_items" SET "sort" = $1, "updated_at" = $2 WHERE "template_items"."id" = $3 [["sort", 2], ["updated_at", "2018-02-20 19:22:56.167142"], ["id", "d7d885b6-2a75-487a-918c-6f3abaae7df1"]]
D, [2018-02-20T20:22:56.175072 #1852] DEBUG -- : SQL (0.7ms) UPDATE "template_items" SET "sort" = $1, "updated_at" = $2 WHERE "template_items"."id" = $3 [["sort", 1], ["updated_at", "2018-02-20 19:22:56.172797"], ["id", "a7ac1687-8cb5-4199-a03b-d7cc975a0387"]]
D, [2018-02-20T20:22:56.176305 #1852] DEBUG -- : (0.6ms) COMMIT
I, [2018-02-20T20:22:56.183481 #1852] INFO -- : Rendered TemplateSerializer with ActiveModelSerializers::Adapter::Attributes (2.97ms)
以下是操作正确返回新数据时的日志-我已标记了差异(1)和(2):
请注意区别:
(1) 日志显示“模板存在”消息
(2) 提交后,Rails重新加载模板_项以从数据库获取更新的数据
我知道我可以修复此问题,并强制更新操作始终执行(2)并重新加载模板\u项子对象:
模板\u controller.rb
class Template < ApplicationRecord
# Relationships
belongs_to :account
has_many :template_items, -> { order('sort ASC') }, dependent: :destroy
accepts_nested_attributes_for :template_items, allow_destroy: true
# Enums
enum template_type: {template: 0, checklist: 1}
enum status: {not_started: 0, started: 1, completed: 2}
# Callbacks
before_save :set_status, unless: :is_template? # only care about status for checklists
def is_template?
return self.template_type == 'template'
end
def set_status
completed = 0
self.template_items.each do |item|
completed += 1 if item.is_completed
end
case completed
when 0
self.status = Template.statuses[:not_started]
when 1..(self.template_items.length - 1)
self.status = Template.statuses[:started]
when self.template_items.length
self.status = Template.statuses[:completed]
end
end
end
class TemplateItem < ApplicationRecord
# Relationships
belongs_to :template
# Validations
validates_presence_of :template
end
def template_params
params.require(:template).
permit(:id, :account_id, :list_type, :name, :title, :info, :status,
template_items_attributes:
[:id, :template_id, :is_completed, :content, :item_type, :sort, :_destroy])
end
def update
if @template.update(template_params)
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
def update
if @template.update(template_params)
@template.template_items.reload
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
class TemplateSerializer < ActiveModel::Serializer
attributes :id,
:account_id,
:name,
:info,
:title,
:template_type,
:status
has_many :template_items, include_nested_associations: true
end
但是,如果Rails能够(有时)自己解决这个问题,为什么我需要这样做呢?虽然缓存在两个调用中都使用,但在正确的第二个示例中,Rails已经发现它需要在数据库更新后重新加载子对象,而不是在第一个示例中
所以我想了解的是,是什么控制了这种行为。在我看来,它必须与模板
模型中的保存之前的操作相关,因为该操作仅针对第二种情况(模板类型为“模板”),而不是第一种情况(模板类型为“检查表”)。换句话说,当该操作触发时,它似乎“改变”了更新操作的行为
因此,我的问题是:
为什么同一个动作有不同的行为?如果是
在你保存之前,为什么
为什么在正确的情况下日志显示模板存在(因为它
这两种情况是否都存在
Rails如何知道在正确的情况下重新加载更新的子级
但不是在不正确的情况下
**更新**
这是模板\u serializer.rb
class Template < ApplicationRecord
# Relationships
belongs_to :account
has_many :template_items, -> { order('sort ASC') }, dependent: :destroy
accepts_nested_attributes_for :template_items, allow_destroy: true
# Enums
enum template_type: {template: 0, checklist: 1}
enum status: {not_started: 0, started: 1, completed: 2}
# Callbacks
before_save :set_status, unless: :is_template? # only care about status for checklists
def is_template?
return self.template_type == 'template'
end
def set_status
completed = 0
self.template_items.each do |item|
completed += 1 if item.is_completed
end
case completed
when 0
self.status = Template.statuses[:not_started]
when 1..(self.template_items.length - 1)
self.status = Template.statuses[:started]
when self.template_items.length
self.status = Template.statuses[:completed]
end
end
end
class TemplateItem < ApplicationRecord
# Relationships
belongs_to :template
# Validations
validates_presence_of :template
end
def template_params
params.require(:template).
permit(:id, :account_id, :list_type, :name, :title, :info, :status,
template_items_attributes:
[:id, :template_id, :is_completed, :content, :item_type, :sort, :_destroy])
end
def update
if @template.update(template_params)
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
def update
if @template.update(template_params)
@template.template_items.reload
render json: @template, serializer: TemplateSerializer, status: :ok
else
render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
end
end
class TemplateSerializer < ActiveModel::Serializer
attributes :id,
:account_id,
:name,
:info,
:title,
:template_type,
:status
has_many :template_items, include_nested_associations: true
end
class TemplateSerializer
这里的问题是您在更改排序之前请求项目。这意味着您拥有的项目数组将不再排序,因为您更改了它们排序的属性。换句话说,在您修改它们之后,没有其他查询返回正确的顺序
因此,我认为可能的解决方案是:
更改排序后重新加载项目
在更改排序之前,不要提取项目
根据更改的排序值,改变模板\u项的顺序
权衡:
您有2个select查询和更新
在选择记录之前,必须使用TemplateItem.update(id,sort:sort)
更新事务中的所有更新
如果您没有呈现所有结果,或者以后决定不呈现,那么您可能正在修改一个不再在页面上的项目。可能还有其他问题
为什么同一个动作有不同的行为?如果是之前的保存,那么为什么
保存前正在请求保存前的模板\u项目
。否则,template\u项
在序列化程序呈现它们之前不会被调用。请注意,这意味着您的保存之前的回调没有按照您希望的方式执行,因为它正在根据以前的值修改状态
为什么日志显示模板在正确的情况下存在(因为它在两种情况下都存在)
看看SQL,这看起来像是一个验证,以确保名称在模板和类型之间是唯一的
Rails如何知道在正确的情况下而不是在错误的情况下重新加载更新的子级
Rails不知道。在这两种情况下,它只加载它们一次。只是,使用before_save,它会在更新记录之前运行
总结:
解决此计时问题的最简单方法是使用不同的回调,该回调在更新子项后触发,例如after\u update
我认为它在第二个上重新加载的原因是因为在保存前“更改”了模板(存在的是一个名称
唯一性验证,几乎可以肯定),因为模板是“变了“它不断地重新加载它的子项,就像模板没有改变,只有子项发生了变化一样,它不需要重新加载。我建议您的已知工作环境始终重新加载,以避免出现这种情况inconsistency@engineersmnky谢谢,这似乎是有道理的。如果可能的话,我想在文档中找到一个官方参考…我会继续寻找。是的,现在我认为我需要解决方法…而且可能永远:)如果您遇到模型被自动重新加载的情况,我希望您的序列化程序中会发生一些事情。你能把那个代码包括进去吗?@CodyGustafson我不认为是那个,但我添加了序列化程序代码。是的,非常瘦的序列化程序。我现在期望更新父级可能会使关联缓存无效。感谢所有这些信息。我取出了之前的保存,现在我