Как я могу вызвать обратный вызов after_save при использовании counter_cache?

У меня есть модель, в которой для ассоциации включен counter_cache:

class Post
  belongs_to :author, :counter_cache => true
end

class Author
  has_many :posts
end

Я также использую фрагмент кеша для каждого «автора», и я хочу, чтобы срок действия этого кеша истекал всякий раз, когда обновляется @author.posts_count, поскольку это значение отображается в пользовательском интерфейсе. Проблема в том, что внутренности counter_cache (increment_counter и decrement_counter), по-видимому, не вызывают обратные вызовы для Author, поэтому у меня нет возможности узнать, когда это произойдет, кроме как истечение срока действия кеша из наблюдателя Post (или очистки кеша) который просто не кажется таким чистым.

Любые идеи?


person Carter    schedule 07.05.2011    source источник


Ответы (5)


Я тоже не мог заставить его работать. В конце концов, я сдался и написал свой собственный метод, похожий на cache_counter, и вызывал его из обратного вызова after_save.

person Dex    schedule 13.06.2011
comment
Спасибо, Декс, я тоже опубликую решение, которое я придумал - person Carter; 14.06.2011

В итоге я оставил cache_counter как есть, но затем заставил истечение срока действия кеша через обратный вызов post_create, например так:

class Post
  belongs_to :author, :counter_cache => true
  after_create :force_author_cache_expiry

  def force_author_cache_expiry
    author.force_cache_expiry!
  end
end

class Author
  has_many :posts

  def force_cache_expiry!
    notify :force_expire_cache
  end
end

тогда force_expire_cache(author) — это метод в моем классе AuthorSweeper, который завершает действие фрагмента кеша.

person Carter    schedule 14.06.2011

Что ж, у меня была та же проблема, и я попал в ваш пост, но я обнаружил, что, поскольку обратные вызовы «after_» и «before_» являются общедоступными методами, вы можете сделать следующее:

class Author < ActiveRecord::Base
  has_many :posts

  Post.after_create do
    # Do whatever you want, but...
    self.class == Post # Beware of this
  end
end

Я не знаю, насколько это стандартно для этого, но методы общедоступны, так что, думаю, все в порядке.

Если вы хотите разделить кеш и модели, вы можете использовать Sweepers.

person Zequez    schedule 05.03.2012

введите здесь описание изображения

У меня также есть требование следить за изменением счетчика. после копания исходного кода рельсов counter_column изменяется через прямое обновление SQL. Другими словами, он не будет запускать обратный вызов (в вашем случае он не будет запускать обратный вызов в модели автора при обновлении публикации).

из исходного кода rails counter_column также был изменен обратным вызовом after_update.

Мой подход заключается в том, чтобы дать рельсам путь вверх, обновить counter_column самостоятельно:

class Post
  belongs_to :author
  after_update :update_author_posts_counter

  def update_author_posts_counter
    # need to update for both previous author and new author

    # find_by will not raise exception if there isn't any record
    author_was = Author.find_by(id: author_id_was) 

    if author_was
      author_was.update_posts_count!
    end
    if author
      author.update_posts_count!
    end
  end
end

class Author
  has_many :posts
  after_update :expires_cache, if: :posts_count_changed? 

  def expires_cache
    # do whatever you want
  end

  def update_posts_count!
    update(posts_count: posts.count)
  end
end
person Yi Feng Xie    schedule 24.01.2017

У меня было аналогичное требование сделать что-то при обновлении счетчика, в моем случае мне нужно было что-то сделать, если счетчик counter_cache превысил определенное значение, мое решение состояло в том, чтобы переопределить метод update_counters следующим образом:

class Post < ApplicationRecord
  belongs_to :author, :counter_cache => true
end

class Author < ApplicationRecord
  has_many :posts

  def self.update_counters(id, counters)
    author = Author.find(id)
    author.do_something! if author.posts_count + counters['posts_count'] >= some_value
    super(id, counters) # continue on with the normal update_counters flow.
  end
end

Дополнительную информацию см. в документации по update_counters.

person Simon L. Brazell    schedule 01.02.2019