Python导入问题与Django管理命令
不管出于什么原因,当我刚接触Python和Django的时候,我在models.py文件的顶部写了一些像这样的导入语句:
from django.contrib import auth
然后我这样使用它:
class MyModel(models.Model):
user = models.ForeignKey(auth.models.User)
# ...
这没问题。过了很久,我写了一个自定义的管理命令,它会这样做:
from myapp.models import MyModel
当我运行我的自定义命令(python manage.py my_command
)时,Python会抱怨说在models.py中声明ForeignKey
的那一行,auth
模块没有models
这个属性。
为了绕过这个问题,我把models.py改成了更常见的写法:
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User)
# ...
有人能告诉我我错过了什么吗?在运行管理命令的时候环境有什么不同吗?还是我一直都做错了?谢谢!
编辑:根据dmitko的猜测,关于循环导入的问题,这里是我在models.py文件中使用的导入语句。我把原来的auth
导入注释掉了,同时展示了唯一一个有外键指向auth用户模型的模型:
import datetime
from django.db import models
# from django.contrib import auth
from django.contrib.auth.models import User
class UserLastVisit(models.Model):
# user = models.ForeignKey(auth.models.User, unique=True)
# ^^^^^^^^^^^^^^^^
# after adding mgmt command, error occurred here; change to the line below
user = models.ForeignKey(User, unique=True)
last_visit = models.DateTimeField(db_index=True)
这是发现问题的管理命令的导入:
import datetime
from django.core.management.base import NoArgsCommand
from core.models import UserLastVisit, AnonLastVisit, Statistic
这是不是在设置一个循环导入的情况呢?
3 个回答
要准确说清楚发生了什么,其实很难,因为我看不到你添加的新 manage.py 命令。不过,我经常在出现循环导入的情况下看到“没有这个属性”的错误,而这种情况几乎总是通过把模块级别的导入改成函数或类级别的导入来解决,就像你在这里做的那样。你可以检查一下这里是否也有类似的情况。
我想如果你使用了 from django.contrib import auth
,这就意味着你在把 auth 这个包当作一个模块来导入,而它里面的内容是由 auth 文件夹里的 __init__.py
文件决定的:
>>> from django.contrib import auth
>>> dir(auth)
['BACKEND_SESSION_KEY', 'ImproperlyConfigured', 'REDIRECT_FIELD_NAME', 'SESSION_
KEY', '__builtins__', '__doc__', '__file__', '__name__', '__path__', 'authentica
te', 'datetime', 'get_backends', 'get_user', 'import_module', 'load_backend', 'l
ogin', 'logout']
你可以去查看 django\contrib\auth
里的 __init__.py
文件,里面会有相同的函数列表。当你使用 from django.contrib.auth.models import User
时,这意味着你在从 auth 包中导入一个子模块,这样就能正常使用了。
顺便说一下,无论我是在控制台运行还是在我的 Django 应用中,我都无法使用 auth.models.User
。
如果有某个随机的模块导入了模块 x.y.z
,那么后面有个人只导入 x.y
时,就会在 x.y
的命名空间里看到一个 z
。
之所以会这样,是因为 import x.y.z
实际上是三个导入语句合在一起的。它的工作原理大概是这样的:
x = __internal_import('x')
x.y = __internal_import('x/y')
x.y.z = __internal_import('x/y/z')
下次有人执行 __internal_import('x/y')
时,他们会得到同一个对象,因为 Python 很聪明,不会重复导入同一个东西。这个对象已经把它的 z
成员指向了 z
模块。
在你的完整应用中,可能有一个模块执行了 import django.contrib.auth.models
。但是在你的简单独立程序中并没有导入那个模块,所以这个名字就没有被赋值。
(注意:__internal_import
并不存在。这只是一个示例。真正的函数有其他名字,你需要去查找。)