Python Django';s ORM Model.objects.raw()查询导致无限递归错误

Python Django';s ORM Model.objects.raw()查询导致无限递归错误,python,django,inheritance,recursion,django-orm,Python,Django,Inheritance,Recursion,Django Orm,我有以下(简化)模型: 现在,我正在尝试查询一些类似这样的对象(一个简化的示例,不必关注为什么首先使用sql): 访问已删除文件[0]的目录会导致第行出现无限递归错误 self._initial_data[field_name] = getattr(self, field_name) 在通用模型中。我知道继承和使用getattr时存在递归问题,但是使用models.Model.\uu\getattribute\uuu(self,field\u name)在这里似乎没有什么区别。然而,真正起作用

我有以下(简化)模型:

现在,我正在尝试查询一些类似这样的对象(一个简化的示例,不必关注为什么首先使用sql):

访问已删除文件[0]的目录会导致第行出现无限递归错误

self._initial_data[field_name] = getattr(self, field_name)
在通用模型中。我知道继承和使用getattr时存在递归问题,但是使用
models.Model.\uu\getattribute\uuu(self,field\u name)
在这里似乎没有什么区别。然而,真正起作用的是:

dirs_of_deleted_files = Directory.objects \
    .filter(files__in=file_ids, files__removed=True) \
    .distinct('id', 'directory_path') \
    .order_by('directory_path')
现在,访问已删除文件[0]的目录不会导致任何无限递归错误


公共模型被其他几个模型继承,并在不同的地方多次实例化,而且
getattr()
在使用此
Directory.objects.raw()方法之前从未引起任何问题。为什么不呢?我怀疑这是Django中的一个bug,但我会保留我的判断。

是的,这个问题在中是已知的

如果访问
\uuu init\uuu()
中的某个字段,则应将其包含在所有
only()
调用中,以避免对查询集中的每个项目再次命中数据库

这是文档的常见结果:

如果您访问每个延迟字段(一次一个,而不是一次访问所有延迟字段),则将从数据库中检索每个延迟字段

如果方法
\uuuu init\uuuu
确实访问任何两个或多个延迟字段(即
.raw()
延迟的字段,
.only()
.defer()
方法延迟的字段),则每次访问都会触发一个数据库查询,并创建一个需要相同字段的新临时实例

问题的简化示例

class SomeModel(models.Model):
    a = models.IntegerField()
    b = models.IntegerField()

    def __init__(self, *args, **kwargs):
        super(SomeModel, self).__init__(*args, **kwargs)
        (self.a, self.b)

class Test(TestCase):
    def test(self):
        SomeModel.objects.create(a=0, b=0)
        SomeModel.objects.only('id')[0]
        # RuntimeError: maximum recursion depth exceeded
修复

您可以通过self中的条件
字段\u name.\uuu dict\uuu
来修复它,因为如果不需要加载字段,有时您可能不需要跟踪它

def track_fields(self, *args):
    for field_name in args:
        if field_name in self.__dict__:
            self._initial_data[field_name] = getattr(self, field_name)

我添加了类Meta,这对于您的示例是必不可少的。相比之下,您还可以简化示例并保持一致性,以便问题仍然可以重现。建议在Stackoverflow时使用这两种方法。幸运的是,你的问题很有趣,可以解决。非常感谢!你是对的,这个例子可以更简单、更完整。很快就会修好的。
class SomeModel(models.Model):
    a = models.IntegerField()
    b = models.IntegerField()

    def __init__(self, *args, **kwargs):
        super(SomeModel, self).__init__(*args, **kwargs)
        (self.a, self.b)

class Test(TestCase):
    def test(self):
        SomeModel.objects.create(a=0, b=0)
        SomeModel.objects.only('id')[0]
        # RuntimeError: maximum recursion depth exceeded
def track_fields(self, *args):
    for field_name in args:
        if field_name in self.__dict__:
            self._initial_data[field_name] = getattr(self, field_name)