【问题标题】:Django - get_or_create triggers RunTimeError: maximum recursion depth exceededDjango - get_or_create 触发 RunTimeError:超出最大递归深度
【发布时间】:2013-07-02 13:03:48
【问题描述】:

这是我在 stackoverflow 上的第一篇文章,因此欢迎对我提出问题的方式提出任何批评。

在我的代码中,我收到此错误:

RuntimeError: maximum recursion depth exceeded

这是代码(内容无关紧要,我只是以最简单的方式重新创建了错误)。基本上我正在尝试覆盖 __init__。如果对象在数据库中,我想做点什么,如果不在,我想做点别的。

class Question(models.Model):

    text = models.CharField(max_length=140)
    asked = models.BooleanField(default=False)

    def __init__(self, text, *args):
        #called the __init__ of the superclass.
        super(Question, self).__init__()

        self, c = Question.objects.get_or_create(text=text)
        if c:
            print 'This question will be asked!'
            self.asked = True
            self.save()
        else:
            print 'This question was already asked'
            assert self.asked == True

调用构造函数时出错:

Question('how are you?')

我知道问题来自 get_or_create 方法。查看错误信息,

---> 12         self, c = Question.objects.get_or_create(text=text)
...
---> 146         return self.get_query_set().get_or_create(**kwargs)
...
---> 464                 obj = self.model(**params)

get_or_create 在某个时候调用对象的构造函数。然后再次调用 get_or_create 等等......

编辑:我想要实现的基本上是能够写:

Question('How are you?')

如果对象在数据库中,则返回该对象,如果不在,则返回新创建(并保存)的对象。而不是这样的:

> try:
>     q = Question.objects.get(text='How are you?')
> except Question.DoesNotExist:
>     q = Question(text='How are you?')
>     q.save()

所以我想实现这一点的唯一方法是重写 __init__。是不可能的还是在概念上是错误的(或两者兼而有之)?谢谢!

【问题讨论】:

  • 我认为你不应该覆盖 init 方法:)
  • 你想用self, c = Question.objects.get_or_create(text=text)做什么?
  • 我刚刚编辑了我的问题,让我的目标更容易理解 :-)

标签: django recursion init depth


【解决方案1】:

您不应该在__init__ 中尝试这样做。 (事实上​​,Django 模型的__init__ 最好不要管。)它应该放在表单中,或者视图中。

在任何情况下,您都不能通过分配给 self 来覆盖自身内部的实例 - 这只是一个与其他任何变量一样的局部变量,并且会在方法结束时超出范围。

另请注意,如果找不到现有实例,您可以使用 defaults 参数到 get_or_create 传递要在新实例上设置的默认值:

question, created = Question.objects.get_or_create(text=text, defaults={'asked': True})

问题更新后编辑您的编辑更清楚地表明__init__ 确实不是这样做的地方。不要忘记,即使评估一个普通的查询集也会实例化模型对象,这意味着调用__init__ - 所以只是从数据库中获取一个实例会遇到问题。不要这样做。

相反,如果你真的需要模型来提供它——即使它只是一行代码,如上所示——你可以定义一个类方法:

class Question(models.Model):
    ...
    @classmethod
    def query(cls, text):
         question, _ = cls.objects.get_or_create(text=text, defaults={'asked': True})
         return question

然后您可以执行Question.query('How are you'),它会返回新项目或现有项目。

【讨论】:

  • 感谢 Daniel 指出覆盖自身内部的实例是没有意义的!但是,我真的希望能够覆盖 _init_ 方法(尽管不建议这样做:-))以便能够编写类似 Question('myquestion') 的内容并让它返回实例+ 在幕后做一些数据库工作。我已经编辑了我的问题,以使我的目标更清晰。
【解决方案2】:

__init__ 中的逻辑需要移动到其他地方,例如视图。 get_or_create 返回两个值:1) 对象和 2) 是否必须创建对象。 See the docs for more details.

def some_view(request):
    c, created = Question.objects.get_or_create(text=text)

    if not created:
        print 'This question was already asked'
    else:
        print 'This question will be asked!'
        c.asked = True
        c.save()

【讨论】:

  • 谢谢斯科特!我知道如果我将那部分代码移到另一个函数或方法(如 my_init)中,所有问题都会消失。但是,我真的想重写 _init_ 以便能够编写 q = Question('myquestion')。我已经编辑了我的问题以使其更清楚。
【解决方案3】:

就像@Scott Woodall 所说,您需要移动初始化逻辑。 这就是发生了什么:

当你调用Question('how are you?') 时,你会转到__init__ 方法。 __init__ 调用Question.objects.get_or_create(text=text),谁找不到Questiontext,所以尝试创建一个新问题,调用__init__(再次)。 您将永远重新进入方法调用。

Question('how are you?') # <-- You call this method
  |
  +-- def __init__(self, text, *args): 
      |
      +-- Question.objects.get_or_create(text=text) # This try to create a model, calling __init__ method!
          |
          +-- def __init__(self, text, *args): 
              |
              +-- Question.objects.get_or_create(text=text) # This try to create a model, calling __init__ method!
                  |
                  +  # So on...

我认为您应该在文本问题字段中添加unique=True

查看@Scott 的回答

【讨论】:

  • 感谢您的宝贵时间。这确实是正在发生的事情!但是,不幸的是,这并不能解决如何覆盖 _init_ 的问题,这样我就可以编写 Question('myquestion') 并在幕后完成一些数据库工作。我已经编辑了我的问题以使其更清楚。
猜你喜欢
  • 2013-02-20
  • 1970-01-01
  • 1970-01-01
  • 2017-03-18
  • 2015-04-15
  • 2023-01-30
  • 1970-01-01
  • 2019-07-03
  • 2013-09-05
相关资源
最近更新 更多