将 Django 版本从 4.0 升级到 4.2.15 后,我收到一个奇怪的错误。 遇到的错误是:
RecursionError: maximum recursion depth exceeded while calling a Python object
。
ModelDiffMixin 如下所示:
from django.forms.models import model_to_dict
class ModelDiffMixin(object):
def __init__(self, *args, **kwargs):
super(ModelDiffMixin, self).__init__(*args, **kwargs)
self.__initial = self._dict
@property
def diff(self):
d1 = self.__initial
d2 = self._dict
diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
return dict(diffs)
@property
def has_changed(self):
return bool(self.diff)
@property
def changed_fields(self):
return self.diff.keys()
def get_field_diff(self, field_name):
"""
Returns a diff for field if it's changed and None otherwise.
"""
return self.diff.get(field_name, None)
def save(self, *args, **kwargs):
"""
Saves model and set initial state.
"""
super(ModelDiffMixin, self).save(*args, **kwargs)
self.__initial = self._dict
@property
def _dict(self):
return model_to_dict(self, fields=[field.name for field in
self._meta.fields])
参考这里的要点:
https://gist.github.com/goloveychuk/72499a7251e070742f00
在此处附加堆栈跟踪。我也浏览了 django 文档,似乎在模型的 init 方法中访问 django 字段可能会导致问题。
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query.py", line 122, in __iter__
obj = model_cls.from_db(
^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/base.py", line 582, in from_db
new = cls(*values)
^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/base.py", line 571, in __init__
super().__init__()
File "/Users/bm/Desktop/Django/bapi/core/utils.py", line 2146, in __init__
self.__initial = self._dict
^^^^^^^^^^
File "/Users/bm/Desktop/Django/bapi/core/utils.py", line 2201, in _dict
return model_to_dict(self, fields=[field.name for field in self._meta.fields])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/forms/models.py", line 115, in model_to_dict
data[f.name] = f.value_from_object(instance)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/fields/__init__.py", line 1088, in value_from_object
return getattr(obj, self.attname)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query_utils.py", line 178, in __get__
instance.refresh_from_db(fields=[field_name])
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/base.py", line 724, in refresh_from_db
db_instance = db_instance_qs.get()
^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
num = len(clone)
^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
self._fetch_all()
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
self._result_cache = list(self._iterable_class(self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
results = compiler.execute_sql(
^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1547, in execute_sql
sql, params = self.as_sql()
^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/psqlextra/compiler.py", line 76, in as_sql
sql, params = super().as_sql(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 734, in as_sql
extra_select, order_by, group_by = self.pre_sql_setup(
^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 85, in pre_sql_setup
order_by = self.get_order_by()
^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 457, in get_order_by
for expr, is_ref in self._order_by_pairs():
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 339, in _order_by_pairs
selected_exprs[expr] = pos_expr
~~~~~~~~~~~~~~^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/expressions.py", line 502, in __hash__
return hash(self.identity)
^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/utils/functional.py", line 57, in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
File "/Users/bm/.virtualenvs/django4.2/lib/python3.11/site-packages/django/db/models/expressions.py", line 479, in identity
constructor_signature = inspect.signature(self.__init__)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.11.9/Frameworks/Python.framework/Versions/3.11/lib/python3.11/inspect.py", line 3263, in signature
return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.11.9/Frameworks/Python.framework/Versions/3.11/lib/python3.11/inspect.py", line 3011, in from_callable
return _signature_from_callable(obj, sigcls=cls,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.11.9/Frameworks/Python.framework/Versions/3.11/lib/python3.11/inspect.py", line 2447, in _signature_from_callable
_get_signature_of = functools.partial(_signature_from_callable,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded while calling a Python object
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在 Django 模型中访问,例如:
class Member(ModelDiffMixin):
class Meta:
index_together = [['facebook_id'], ['enrollment_referrer']]
indexes = [models.Index(fields=['-created_ts'])]
member_name = models.CharField(max_length=100, null=True, blank=True)
middle_name = models.CharField(max_length=100, null=True, blank=True)
代码中没有进行任何更改,相同的代码似乎适用于 Django 4.0,但不适用于 Django 4.2.15。如果有人可以帮忙解决这个问题,我将非常感激。
就像 Ben 在评论中所说的那样,
self.__inital = self._dict
函数中的 init()
导致了递归,因为 _dict
尚未初始化。一种方法是将其放入 save()
方法中。
也许尝试做这样的事情:
from django.forms.models import model_to_dict
class ModelDiffMixin(object):
def __init__(self, *args, **kwargs):
super(ModelDiffMixin, self).__init__(*args, **kwargs)
self.__initial = None
def save(self, *args, **kwargs):
super(ModelDiffMixin, self).save(*args, **kwargs)
self.__initial = self._dict
@property
def diff(self):
if self.__initial is None:
return {}
d1 = self.__initial
d2 = self._dict
diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
return dict(diffs)
@property
def has_changed(self):
return bool(self.diff)
@property
def changed_fields(self):
return self.diff.keys()
def get_field_diff(self, field_name):
"""
Returns a diff for field if it's changed and None otherwise.
"""
return self.diff.get(field_name, None)
@property
def _dict(self):
return model_to_dict(self, fields=[field.name for field in self._meta.fields])
def refresh_initial_state(self):
self.__initial = self._dict
请花点时间完善代码,它不会是完美的,因为我不知道代码的其余部分是什么。如果您能分享一个有效的示例,我可以更好地了解。