Django cached queryset.last() возвращает объект None для объекта не None

Мой код:

@ttl_cache(maxsize=50, ttl=60 * 60)
def get_query_set():
  return Model.objects.all().order_by('year')

def get_latest():
  cached_qs = get_query_set()
  try:
    return cached_qs.last().year if len(cached_qs) > 0 else None
  except AttributeError:
    print('~~~~~~~~~~~~~~~~~')
    print('CHECK CIRCLECI PROBLEM')
    print(cached_qs)
    print(cached_qs.last())
    print(len(cached_qs))
    for aa in cached_qs:
      print(aa)
      print(type(aa))
      print(dir(aa))
      print(hasattr(aa, 'year'))
      try:
        print(aa.year)
      except Exception:
        print('no attr for aa')
    print(Model.objects.all().order_by('year'))
    for aaa in Model.objects.all().order_by('year'):
      print(aaa)
      print(aaa.year)
    print('~~~~~~~~~~~~~~~~~')
    raise

Без try-except я получаю (на get_latest())

AttributeError: 'NoneType' object has no attribute 'year'

Когда я ловлю исключение, он выводит:

~~~~~~~~~~~~~~~~~
CHECK CIRCLECI PROBLEM
<QuerySet [<Model: Model object (2)>]>
None
1
Model object (2)
<class 'path.models.model.Model'>
['DoesNotExist', 'Meta', 'MultipleObjectsReturned', '__class__', '__delattr__', ..., 'year']
True
2019
<QuerySet []>
~~~~~~~~~~~~~~~~~

Итак, мы видим, что

  1. this problem is reproducible only at CircleCI build sometimes (I don't know how to reproduce it at local machine)
  2. cached queryset is not empty
  3. length of cached queryset is 1
  4. queryset object is not None and has attribute year
  5. value of year is 2019
  6. real object is already deleted at DB and does not exist (physically DB table is empty)
  7. non cached queryset is empty (because of ⑥)

Я предполагаю, что last() пытается повторно получить объект из БД, даже если queryset кэширован (из-за ленивого поведения queryset?). Но я не понимаю, почему это происходит? И что я должен ожидать от вывода last()?

Я пытался удалить объект Model из DB вручную после кэширования python queryset, но last() метод работает как ожидалось и возвращает правильное значение.

Есть идеи?

Вернуться на верх