这是我关于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__。不可能或概念上是错误的(或两者兼而有之)?谢谢!
答案 0 :(得分: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')
,它会返回新项目或现有项目。
答案 1 :(得分:0)
__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()
答案 2 :(得分: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答案