Ruby on rails Rails 5 API-控制器更新操作有时不反映数据库更改(缓存问题)?

Ruby 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

我有两个简单的模型,有很多关系。一个模板有许多模板项。模板的模板类型可以是两个值(“模板”或“检查表”)中的一个

为了简洁起见,我删除了不相关的代码

模板.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
请注意,项的一个属性称为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我不认为是那个,但我添加了序列化程序代码。是的,非常瘦的序列化程序。我现在期望更新父级可能会使关联缓存无效。感谢所有这些信息。我取出了之前的保存,现在我