Я использую модели базы данных Django от процесса, которым это не называют из Запроса HTTP. Процесс, как предполагается, опрашивает относительно новых данных каждые несколько секунд и делает некоторую обработку на нем. У меня есть цикл, который спит в течение нескольких секунд и затем получает все необработанные данные из базы данных.
То, что я вижу, - то, что после первой выборки, процесс никогда не видит новых данных. Я запустил несколько тестов, и похоже, что Django кэширует результаты, даже при том, что я создаю новый QuerySets каждый раз. Для проверки этого я сделал это от оболочки Python:
>>> MyModel.objects.count()
885
# (Here I added some more data from another process.)
>>> MyModel.objects.count()
885
>>> MyModel.objects.update()
0
>>> MyModel.objects.count()
1025
Как Вы видите, добавляя, что новые данные не изменяют количество результата. Однако называя обновление менеджера () метод, кажется, решает проблему.
Я не могу найти документацию относительно того обновления () методом и понятия не иметь, что другие плохие вещи это могло бы сделать.
Мой вопрос, почему я вижу это поведение кэширования, которое противоречит тому, что говорят документы Django? И как я предотвращаю его?
Похоже, что count ()
попадает в кеш после первого раза. Это исходный код django для QuerySet.count:
def count(self):
"""
Performs a SELECT COUNT() and returns the number of records as an
integer.
If the QuerySet is already fully cached this simply returns the length
of the cached results set to avoid multiple SELECT COUNT(*) calls.
"""
if self._result_cache is not None and not self._iter:
return len(self._result_cache)
return self.query.get_count(using=self.db)
update
, похоже, делает довольно много дополнительной работы помимо того, что вам нужно.
Но я не могу придумать лучшего способа сделать это, кроме написания собственного SQL для подсчета.
Если производительность не очень важна, я бы просто сделал то, что делаете вы, вызвав update
до count
.
QuerySet.update:
def update(self, **kwargs):
"""
Updates all elements in the current QuerySet, setting all the given
fields to the appropriate values.
"""
assert self.query.can_filter(), \
"Cannot update a query once a slice has been taken."
self._for_write = True
query = self.query.clone(sql.UpdateQuery)
query.add_update_values(kwargs)
if not transaction.is_managed(using=self.db):
transaction.enter_transaction_management(using=self.db)
forced_managed = True
else:
forced_managed = False
try:
rows = query.get_compiler(self.db).execute_sql(None)
if forced_managed:
transaction.commit(using=self.db)
else:
transaction.commit_unless_managed(using=self.db)
finally:
if forced_managed:
transaction.leave_transaction_management(using=self.db)
self._result_cache = None
return rows
update.alters_data = True
Вы также можете использовать MyModel.objects._clone().count().
Все методы в QuerySet
вызывают _clone()
перед выполнением любой работы - это гарантирует, что все внутренние кэши будут аннулированы.
Основная причина заключается в том, что MyModel.objects
каждый раз является одним и тем же экземпляром. Клонируя его, вы создаете новый экземпляр без кэшированного значения. Конечно, вы всегда можете зайти и аннулировать кэш, если хотите использовать один и тот же экземпляр.