0

这是我在 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__。是不可能的还是在概念上是错误的(或两者兼而有之)?谢谢!

4

3 回答 3

2

你不应该在__init__. (事实上​​,最好不要管__init__Django 模型。)它应该放在表单或视图中。

在任何情况下,您都不能通过赋值来覆盖自身内部的实例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'),它会返回新的或现有的项目。

于 2013-07-02T13:26:51.190 回答
0

您需要将逻辑__init__移动到其他地方,例如视图。get_or_create返回两个值:1) 对象和 2) 是否必须创建对象。有关更多详细信息,请参阅文档。

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()
于 2013-07-02T13:20:19.103 回答
0

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

当你打电话时Question('how are you?'),你去__init__方法。__init__呼叫,Question.objects.get_or_create(text=text)谁没有找到一个Question与那个text,所以尝试创建一个新的问题,呼叫__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 的答案

于 2013-07-02T13:32:39.570 回答