代码之家  ›  专栏  ›  技术社区  ›  Saeid

使用模型的子类作为该模型的选择选项会引发NameError

  •  0
  • Saeid  · 技术社区  · 6 年前

    我们正在尝试将遗留代码的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"
    
    ...
    

    此代码在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
    

    我理解这个问题我不明白的是django1.8是如何工作的?django1.9中的哪些变化导致了这种情况?最好的解决方法是什么?

    3 回复  |  直到 6 年前
        1
  •  1
  •   bignose    6 年前

    我可以复制这个;使用django1.8, python3 -m manage check NameError .

    没有什么特别之处 the Django 1.9 release notes

    我将解释django1.8的行为,指出Django是臭名昭著的,因为它对代码做了魔术,以便允许引用代码中尚未执行的部分来定义模型。这是Python正常行为的一个异常,也是一个没有文档记录的异常。

    因此,在没有文档记录的情况下,你不应该指望这是一个正常的行为 引用时 Question 在它被定义之前。

    django1.9显然做了一个更改,它恢复了预期的Python行为:-)

        2
  •  0
  •   trinchet    6 年前

    _get_descendant_question_classes 定义了,类 Question 尚未定义。所以,你指的是不存在的东西。

    这里有一个循环依赖性问题: _获取后代问题类 问题 问题 _获取后代问题类

    一个快速的解决办法是 get_model :

    def _get_descendant_question_classes():
        stack = [get_model('yourappnamehere', 'Question')]
    

    type 但我相信你能解决你原来的问题(是什么让你加上这个 类型 字段)以另一种更简单的方式。

    另外,如果您需要知道 问题 实例,只需检查对象是否具有 textquestion_ptr selectquestion_ptr 或者只是使用 isinstance

        3
  •  0
  •   blhsing    6 年前

    问题是,当类变量 type 在中初始化 Question 同学们 问题 _get_question_choices() ,它引用 问题 类,立即求值以便将值赋给 类型 choices ,导致循环引用。

    为了避免这个问题,而不是评估 _获取问题选择() 类型 有空的 选择 首先,然后在 __init__ 方法,它只在 问题 已实例化:

    class Question(models.Model):
        type = models.CharField(max_length=10, choices=(,), default=slug)
    
        def __init__(self, *args, **kwargs):
            super(Question, self).__init__(*args, **kwargs)
            self._meta.get_field('type').choices = _get_question_choices()