我们正在尝试将旧代码的Django版本从1.8升级到1.9。我们有一个这样定义的模型:
def _get_descendant_question_classes():
stack = [Question]
while stack:
cls = stack.pop()
stack.extend(cls.__subclasses__())
yield cls
def _get_question_choices():
question_classes = _get_descendant_question_classes()
for cls in question_classes:
yield (cls.slug, cls._meta.verbose_name)
class Question(models.Model):
slug = "Question"
type = models.CharField(max_length=10, choices=_get_question_choices(), default=slug)
class TextQuestion(Question):
slug = "TextQuestion"
class SelectQuestion(Question):
slug = "SelectQuestion"
...
基本上,模型希望将其子类用作其字段之一的选择选项。它通过以DFS方式遍历模型并产生所有子类来实现。
此代码可在django 1.8中工作,但在django 1.9中会出现此错误:
Traceback (most recent call last):
File "./manage.py", line 16, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 350, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 324, in execute
django.setup()
File "/usr/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
app_config.import_models(all_models)
File "/usr/local/lib/python2.7/site-packages/django/apps/config.py", line 202, in import_models
self.models_module = import_module(models_module_name)
File "/usr/local/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/home/saeed/saeed/survey/models.py", line 85, in <module>
class Question(models.Model):
File "/home/saeed/saeed/survey/models.py", line 99, in Question
type = models.CharField(max_length=10, choices=_get_question_choices(), default=slug)
File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 1072, in __init__
super(CharField, self).__init__(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 161, in __init__
choices = list(choices)
File "/home/saeed/saeed/survey/models.py", line 65, in _get_question_choices
for cls in question_classes:
File "/home/saeed/saeed/survey/models.py", line 54, in _get_descendant_question_classes
stack = [Question]
NameError: global name 'Question' is not defined
我了解问题,我不了解的是在django 1.8中它是如何工作的? django 1.9中发生了什么变化,导致了这一点?解决此问题的最佳方法是什么?
答案 0 :(得分:1)
我可以复制这个;在Django 1.8中,import Vue from 'vue'
import { mount } from '@vue/test-utils'
import { expect } from 'chai'
import sinon from 'sinon'
import Item from '../src/components/Item.vue'
import Vuex from 'vuex'
Vue.use(Vuex);
describe('Item.vue', () => {
let componentProps = {};
let wrapper;
let actions;
let store;
beforeEach(() => {
let name = 'Item Name';
// mock vuex action on click
actions = {
filterData: sinon.stub()
}
let moduleA = {
state: {},
actions
}
store = new Vuex.Store({
modules: {
moduleA
}
});
componentProps.itemName = name;
wrapper = mount(Item, {
store: store,
propsData: componentProps
});
})
it('Has a root element of list-item', () => {
expect(wrapper.is('.list-item')).to.equal(true);
})
it('Item getting prop name', () => {
expect(wrapper.text()).to.equal('Item Name');
})
it('Item is not active on load', () => {
expect(wrapper.vm.$data.isActive).to.equal(false);
})
it('Item is active after click', () => {
wrapper.trigger('click');
expect(wrapper.vm.$data.isActive).to.equal(true);
})
it('Item is not active after two clicks', () => {
wrapper.trigger('click');
wrapper.trigger('click');
expect(wrapper.vm.$data.isActive).to.equal(false);
})
})
成功,而在Django 1.9中它引发了python3 -m manage check
。
the Django 1.9 release notes中没有什么能解决这种行为上的变化。
我将通过解释Django在代码上做“魔术”而臭名昭著,以便允许引用代码中尚未执行的部分来定义模型,以此来解释Django 1.8的行为。这是正常Python行为的异常,而AFAICT是未记录的异常。
因此,这将是偶然的且未记录的行为(因此不应依赖),在普通的Python中,在定义NameError
之前引用NameError
时会期望Question
。 / p>
Django 1.9显然进行了更改,恢复为预期的Python行为:-)
答案 1 :(得分:0)
该异常是由于在定义方法_get_descendant_question_classes
时尚未定义类Question
。因此,您指的是不存在的东西。
这里有一个设计问题,请注意您具有周期性依赖关系:_get_descendant_question_classes
依赖于Question
,Question
依赖于_get_descendant_question_classes
。
一种快速的解决方法是使用get_model
:
def _get_descendant_question_classes():
stack = [get_model('yourappnamehere', 'Question')]
我不确定您为什么需要该type
字段,但是我相信您可以用另一种更简单的方法解决您的原始问题(导致您添加该type
字段)。
此外,如果您需要了解Question
实例的“类型”,则只需检查对象是否具有attr textquestion_ptr
或selectquestion_ptr
或只使用{ {1}}
答案 2 :(得分:0)
问题在于,当在type
类中初始化类变量Question
时,尚未创建Question
类,而尚未创建_get_question_choices()
引用Question
类,立即进行求值,以便为type
的{{1}}分配值,从而产生循环引用。
为避免此问题,可以在类声明时立即对choices
进行初始化,而无需在类声明期间立即评估_get_question_choices()
,然后在{{1 }}的type
方法,仅在实例化choices
之后才调用:
Question