【问题标题】:Running python manage.py test gives "Maximum Recursion Depth reached" error运行 python manage.py test 给出“达到最大递归深度”错误
【发布时间】:2017-06-27 10:03:44
【问题描述】:

所以我有一个基于 1.6.5 构建的 django 项目,现在我将其迁移到 1.9.5。我成功将其迁移到 1.7.0,然后迁移到 1.8.0。从 1.8.0 到 1.9.0 这样做时,我不得不用 collections.OrderedDict 替换 SortedDict。现在我在执行 python manage.py 测试时遇到了这个错误:

    File "forum/models/base.py", line 134, in iterator
    key_list = [v[0] for v in self.values_list(*values_list)]
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 258, in __iter__
    self._fetch_all()
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 1074, in _fetch_all
    self._result_cache = list(self.iterator())
  File "forum/models/base.py", line 134, in iterator
    key_list = [v[0] for v in self.values_list(*values_list)]
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 258, in __iter__
    self._fetch_all()
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 1074, in _fetch_all
    self._result_cache = list(self.iterator())
  File "forum/models/base.py", line 134, in iterator
    key_list = [v[0] for v in self.values_list(*values_list)]
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 725, in values_list
    clone = self._values(*fields)
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 671, in _values
    clone = self._clone()
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/query.py", line 1059, in _clone
    query = self.query.clone()
  File "venv/mystuff/lib/python2.7/site-packages/django/db/models/sql/query.py", line 298, in clone
    obj._annotations = self._annotations.copy() if self._annotations is not None else None
  File "/opt/python/python-2.7/lib64/python2.7/collections.py", line 194, in copy
    return self.__class__(self)
  File "/opt/python/python-2.7/lib64/python2.7/collections.py", line 57, in __init__
    self.__update(*args, **kwds)
  File "venv/mystuff/lib64/python2.7/abc.py", line 151, in __subclasscheck__
    if subclass in cls._abc_cache:
  File "venv/mystuff/lib64/python2.7/_weakrefset.py", line 72, in __contains__
    wr = ref(item)
RuntimeError: maximum recursion depth exceeded

其他类似问题的解决方案要求将 python 升级到 2.7.5,但我已经在 2.7.11 上运行它。

编辑: 论坛/模型/base.py

def iterator(self):
    cache_key = self.model._generate_cache_key("QUERY:%s" % self._get_query_hash())
    on_cache_query_attr = self.model.value_to_list_on_cache_query()

    to_return = None
    to_cache = {}

    with_aggregates = len(self.query.aggregates) > 0
    key_list = self._fetch_from_query_cache(cache_key)

    if key_list is None:
        if not with_aggregates:
            values_list = [on_cache_query_attr]

            if len(self.query.extra):
                values_list += self.query.extra.keys()

            key_list = [v[0] for v in self.values_list(*values_list)] #Line 134
            to_cache[cache_key] = (datetime.datetime.now(), key_list)
        else:
            to_return = list(super(CachedQuerySet, self).iterator())
            to_cache[cache_key] = (datetime.datetime.now(), [
                (row.__dict__[on_cache_query_attr], dict([(k, row.__dict__[k]) for k in self.query.aggregates.keys()]))
                for row in to_return])
    elif with_aggregates:
        tmp = key_list
        key_list = [k[0] for k in tmp]
        with_aggregates = [k[1] for k in tmp]
        del tmp

    if (not to_return) and key_list:
        row_keys = [self.model.infer_cache_key({on_cache_query_attr: attr}) for attr in key_list]
        cached = cache.get_many(row_keys)

        to_return = [
            (ck in cached) and self.obj_from_datadict(cached[ck]) or ToFetch(force_unicode(key_list[i])) for i, ck in enumerate(row_keys)
        ]

        if len(cached) != len(row_keys):
            to_fetch = [unicode(tr) for tr in to_return if isinstance(tr, ToFetch)]

            fetched = dict([(force_unicode(r.__dict__[on_cache_query_attr]), r) for r in
                          models.query.QuerySet(self.model).filter(**{"%s__in" % on_cache_query_attr: to_fetch})])

            to_return = [(isinstance(tr, ToFetch) and fetched[unicode(tr)] or tr) for tr in to_return]
            to_cache.update(dict([(self.model.infer_cache_key({on_cache_query_attr: attr}), r._as_dict()) for attr, r in fetched.items()]))

        if with_aggregates:
            for i, r in enumerate(to_return):
                r.__dict__.update(with_aggregates[i])


    if len(to_cache):
        cache.set_many(to_cache, 60 * 60)

    if to_return:
        for row in to_return:
            if hasattr(row, 'leaf'):
                row = row.leaf

            row.reset_original_state()
            yield row

django/db/models/query.py:

def _fetch_all(self):
    if self._result_cache is None:
        self._result_cache = list(self.iterator()) #Line 1074
    if self._prefetch_related_lookups and not self._prefetch_done:
        self._prefetch_related_objects()

django/db/models/query.py

def __iter__(self):
    """
    The queryset iterator protocol uses three nested iterators in the
    default case:
        1. sql.compiler:execute_sql()
           - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
             using cursor.fetchmany(). This part is responsible for
             doing some column masking, and returning the rows in chunks.
        2. sql/compiler.results_iter()
           - Returns one row at time. At this point the rows are still just
             tuples. In some cases the return values are converted to
             Python values at this location.
        3. self.iterator()
           - Responsible for turning the rows into model objects.
    """
    self._fetch_all() #Line 258
    return iter(self._result_cache)

更新: 我的 Django 日志显示如下:

/forum/settings/base.py TIME: 2017-06-27 06:49:53,410 MSG: base.py:value:65 Error retrieving setting from database (FORM_EMPTY_QUESTION_BODY): maximum recursion depth exceeded in cmp
/forum/settings/base.py TIME: 2017-06-27 06:49:53,444 MSG: base.py:value:65 Error retrieving setting from database (FORM_MIN_NUMBER_OF_TAGS): maximum recursion depth exceeded

【问题讨论】:

  • 能否提供manage.pysn-p对错误负责?
  • 能否提供forum/models/base.py", line 134的完整方法。它看起来像递归。
  • @Melvyn 我已经用这些方法编辑了问题。
  • 尝试在 Django 1.9 和 1.8 之间进行 git bisect 以找出导致代码停止工作的 Django 更改。这可能会提示您如何修复您的方法。
  • @Alasdair 你建议对我的项目或 django 进行一分为二吗?从 1.7 改到 1.8 时,即使我没有提交,我也可以做一个 bisect 吗?

标签: python django django-upgrade


【解决方案1】:

所以你从你的迭代器调用values_list(),它克隆了QuerySet,它迭代了QuerySet,它调用你的迭代器,它克隆了Queryset...

API 已在 this commit 中更改。它应该为您提供足够的信息来重新实现您的查询集。

另一方面,看起来 Django 自己实现了查询缓存,因此在重构之前,您可能会先看看您的 CachedQuerySet 是否已过时。

【讨论】:

  • 对不起,我是个菜鸟,但似乎要使用那个提交,我必须将 django 升级到 1.11.2,我目前不想要那个。此外,如果我决定更改 QuerySet,我将更改官方 django 代码,这是我不想要的。我在 models/base.py 中的方法是否有问题,或者我可以更改其中的某些内容以使代码正常工作吗?
  • 不,github不是很清楚。这是在 1.9 alpha 1 中引入的。因此,您当前正在测试的是 1.9。它列出了使您的工作不再起作用的更改。绿色是新代码,您可以看到 iterator 方法现在应该返回一组固定值的迭代器,以避免这种无限递归。
  • 查看问题中提供的我当前的迭代器方法,您建议进行哪些更改以便能够返回一组固定值的迭代器?
猜你喜欢
  • 2013-04-28
  • 2021-12-11
  • 2017-10-25
  • 2014-05-08
  • 2017-08-24
  • 1970-01-01
  • 2016-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多