在Django管理命令中正确导入
Correctly import in Django management command
自定义管理命令,oauth.py,需要来自另一个模块的模型。当我包含 "from appname.authentication.models import Contact" 时,我得到 "AttributeError: 'module' object has no attribute 'models'." - 我一直停留在 django 1.6 上,直到我能够构建一个测试套件来帮助升级。
如何正确导入联系人?
其他值得注意的 SO 答案:
- Circular Import
- Import Settings
除 /app 之外的每个目录都有一个 __init__.py
。 /app在sys.path/django目录下,/app:
util
-management
--commands
---oauth.py
appname
-authentication
--models.py
extouth.py
extoauth.py 是具有相同导入和工作的独立脚本,但仅在 manage.py shell 中有效。自定义管理命令会更好
oauth.py:
import sys
from optparse import make_option
from provider.oauth2.models import Client
from appname.authentication.models import Contact
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Creates OAUTH user and gets access token.'
option_list = BaseCommand.option_list + (
make_option('--create-client',
dest='create_client',
help='''Returns tuple of <id,secret>...'''),
make_option('--get-access-token',
dest='get_access_token',
help='''Returns time limited access token...'''),
)
def handle(self, *args, **options):
if options['create_client']:
return self.create_client(options['create_client'])
elif options['get_access_token']:
self.get_access_token()
def create_client(self, user):
return user
def get_access_token(self):
pass
控制台输出:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 75, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/usr/local/lib/python2.7/site-packages/django/utils/importlib.py", line 40, in import_module
__import__(name)
File "/app/waapiutil/management/commands/oauth.py", line 4, in <module>
from wowza.authentication.models import Contact
File "/app/wowza/authentication/models.py", line 80, in <module>
class
SalesforceModel(with_metaclass(salesforce.models.SalesforceModelBase, models.Model)):
AttributeError: 'module' object has no attribute 'models'
hypo - 设置未导入
所以我的设置必须像使用 manage.py shell 一样进行设置,因为如果我在文件顶部包含:
从 django.conf 导入设置
settings.configure()
我得到:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 75, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/usr/local/lib/python2.7/site-packages/django/utils/importlib.py", line 40, in import_module
__import__(name)
File "/app/waapiutil/management/commands/oauth.py", line 2, in <module>
settings.configure()
File "/usr/local/lib/python2.7/site-packages/django/conf/__init__.py", line 89, in configure
raise RuntimeError('Settings already configured.')
RuntimeError: Settings already configured.
hypo - 更深层次的语法错误(无论如何都应该破坏生产)
在我的应用程序文件中搜索出现的 models.model 会产生四个结果,每个结果都有正确的大写 models.Model.
hypo - 联系人已导入
当我注释掉导入和 运行 我得到的命令时:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 242, in run_from_argv
self.execute(*args, **options.__dict__)
File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 285, in execute
output = self.handle(*args, **options)
File "/app/waapiutil/management/commands/oauth.py", line 23, in handle
return self.create_client(options['create_client'])
File "/app/waapiutil/management/commands/oauth.py", line 32, in create_client
c = Client(user=Contact.objects.get_by_email(e), name=n,
NameError: global name 'Contact' is not defined
来自 authentication/models.py 的片段以供 hynekcer 的评论
# Core Django imports
from django.db import models
from django.core.validators import MinLengthValidator
from django.utils.six import with_metaclass
# Third-party imports
import pycountry
from rest_framework.compat import oauth2_provider
import salesforce
from salesforce import fields
from salesforce.backend import manager
...
class SalesforceManager(manager.SalesforceManager):
"""
Override the default Salesforce manager so we can get some proper REST framework exceptions
"""
def get(self, *args, **kwargs):
try:
result = self.get_queryset().get(*args, **kwargs)
except self.model.MultipleObjectsReturned:
raise MultipleUniqueRecords()
except Exception as e:
logger.warning("SalesForce exception %s", str(e))
raise NoRecord()
return result
class SalesforceModel(with_metaclass(salesforce.models.SalesforceModelBase, models.Model)):
"""
Abstract model class for Salesforce objects.
"""
_base_manager = objects = SalesforceManager()
_salesforce_object = True
class Meta:
managed = False
abstract = True
Id = fields.SalesforceAutoField(primary_key=True)
def clean_fields(self, *args, **kwargs):
# Override the default clean_fields method so we can catch validation exceptions
try:
super(SalesforceModel, self).clean_fields(*args, **kwargs)
except Exception as validation_exception:
detail = ''
for field, message in validation_exception.error_dict.items():
detail += field + ': ' + message[0].messages[0]
raise ValidationError(detail)
def save(self, *args, **kwargs):
# Override the default save method so we can remove fields that Salesforce manages
if self._meta.model_name in ['contact', 'account', 'license', 'sslcertificate']:
if not self.Id:
for field in self._meta.fields:
if field.attname == 'created_date' or field.attname == 'certificate_id':
self._meta.fields.remove(field)
else:
update_fields = self._meta.get_all_field_names()
remove_list = []
if self._meta.model_name == 'contact':
remove_list = ['created_date', 'accesstoken', 'refreshtoken', 'oauth2_client', 'grant', 'Id', 'entitlement_plan']
elif self._meta.model_name == 'account':
remove_list = ['created_date', 'account', 'Id']
elif self._meta.model_name == 'license':
remove_list = ['created_date', 'Id']
elif self._meta.model_name == 'sslcertificate':
remove_list = ['certificate_id', 'created_date', 'Id']
for remove_field in remove_list:
if remove_field in update_fields:
update_fields.remove(remove_field)
kwargs['update_fields'] = update_fields
# Retry five times if there's a SalesforceError
delay = 1
for retry in range(5):
try:
super(SalesforceModel, self).save(*args, **kwargs)
break
except Exception as e:
logger.error("Saving {0} resulted in an error {1}, retry {2}".format(str(self),str(e),retry))
if retry < 4 and "SERVER_UNAVAILABLE" in str(e):
time.sleep(delay)
delay *= 2
else:
(此处评论占位较大,暂未回复。)
当我在您的应用程序代码中看到来自我的数据库后端的一行时,我以为我的眼睛在骗人,也许在同一模块中还有一个具体模型。我看到您粘贴的代码基于旧的 django-salesforce 0.5.x 代码。好的,希望你也使用包 0.5.x。它可能无法与当前的 django-salesforce 0.6 一起使用,或者没有人会期待它
最安全的样式是使用publicAPI。一种更脆弱的风格是也使用未记录的功能。最脆弱的是从其他包的核心复制粘贴代码而忘记你开始使用新版本。如果你这样做,你必须有足够严肃的理由和足够的时间。 (我为每个 Django 版本做了六次,因为 django-salesforce 后端对我来说足够有趣,但我不推荐给你。)
你可以简单的写:
class MySalesforceModel(salesforce.models.SalesforceModel):
def ... # your custom method
class Meta:
managed = False
abstract = True
优点是这将使用正确的当前 SalesforceModel,即使是下一个版本的 Django 和 django-salesforce。
如果您确实需要自定义后端,请将后端和应用程序代码合理分离到不同的模块中,以便在不导入任何具体模型的情况下导入后端。否则你很容易得到丑陋的依赖。此外,一个非空的 'init.py` 会导致愚蠢的依赖关系
如果你有完整的源代码,你不需要假设("hypo")这样简单的事情。您可以简单地记录(打印)该信息,例如放置 import sys; print("settings imported", "my_project.settings" in sys.modules)
或验证设置是否正确配置 - 在有问题的地方之前临时放置一行:from django.conf import settings; print(settings.INSTALLED_APPS) # any valid name
你应该在寻求帮助之前检查语法错误。对于子目录树的任何特定 Python 版本都可以轻松完成,例如通过 python2.7 -m compileall $(pwd)
使用通用处理 except Exception:
非常疯狂,尤其是在调试时。想象一下 try...except 之间的代码会引发 ImportError 而你吞下了它。
通过重构代码来解决依赖关系比以后维护脆弱的代码更容易。想象一下,如果您在一个函数中导入,并且只有在模块仅从 Apache 中的线程导入时,您才会从您的朋友那里收到一条奇怪的 ImportError 消息,但如果您从主线程导入它,它就会成功。支持显式导入。
自定义管理命令,oauth.py,需要来自另一个模块的模型。当我包含 "from appname.authentication.models import Contact" 时,我得到 "AttributeError: 'module' object has no attribute 'models'." - 我一直停留在 django 1.6 上,直到我能够构建一个测试套件来帮助升级。 如何正确导入联系人?
其他值得注意的 SO 答案:
- Circular Import
- Import Settings
除 /app 之外的每个目录都有一个 __init__.py
。 /app在sys.path/django目录下,/app:
util
-management
--commands
---oauth.py
appname
-authentication
--models.py
extouth.py
extoauth.py 是具有相同导入和工作的独立脚本,但仅在 manage.py shell 中有效。自定义管理命令会更好
oauth.py:
import sys
from optparse import make_option
from provider.oauth2.models import Client
from appname.authentication.models import Contact
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Creates OAUTH user and gets access token.'
option_list = BaseCommand.option_list + (
make_option('--create-client',
dest='create_client',
help='''Returns tuple of <id,secret>...'''),
make_option('--get-access-token',
dest='get_access_token',
help='''Returns time limited access token...'''),
)
def handle(self, *args, **options):
if options['create_client']:
return self.create_client(options['create_client'])
elif options['get_access_token']:
self.get_access_token()
def create_client(self, user):
return user
def get_access_token(self):
pass
控制台输出:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 75, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/usr/local/lib/python2.7/site-packages/django/utils/importlib.py", line 40, in import_module
__import__(name)
File "/app/waapiutil/management/commands/oauth.py", line 4, in <module>
from wowza.authentication.models import Contact
File "/app/wowza/authentication/models.py", line 80, in <module>
class
SalesforceModel(with_metaclass(salesforce.models.SalesforceModelBase, models.Model)):
AttributeError: 'module' object has no attribute 'models'
hypo - 设置未导入 所以我的设置必须像使用 manage.py shell 一样进行设置,因为如果我在文件顶部包含: 从 django.conf 导入设置 settings.configure()
我得到:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 75, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/usr/local/lib/python2.7/site-packages/django/utils/importlib.py", line 40, in import_module
__import__(name)
File "/app/waapiutil/management/commands/oauth.py", line 2, in <module>
settings.configure()
File "/usr/local/lib/python2.7/site-packages/django/conf/__init__.py", line 89, in configure
raise RuntimeError('Settings already configured.')
RuntimeError: Settings already configured.
hypo - 更深层次的语法错误(无论如何都应该破坏生产) 在我的应用程序文件中搜索出现的 models.model 会产生四个结果,每个结果都有正确的大写 models.Model.
hypo - 联系人已导入 当我注释掉导入和 运行 我得到的命令时:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 242, in run_from_argv
self.execute(*args, **options.__dict__)
File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 285, in execute
output = self.handle(*args, **options)
File "/app/waapiutil/management/commands/oauth.py", line 23, in handle
return self.create_client(options['create_client'])
File "/app/waapiutil/management/commands/oauth.py", line 32, in create_client
c = Client(user=Contact.objects.get_by_email(e), name=n,
NameError: global name 'Contact' is not defined
来自 authentication/models.py 的片段以供 hynekcer 的评论
# Core Django imports
from django.db import models
from django.core.validators import MinLengthValidator
from django.utils.six import with_metaclass
# Third-party imports
import pycountry
from rest_framework.compat import oauth2_provider
import salesforce
from salesforce import fields
from salesforce.backend import manager
...
class SalesforceManager(manager.SalesforceManager):
"""
Override the default Salesforce manager so we can get some proper REST framework exceptions
"""
def get(self, *args, **kwargs):
try:
result = self.get_queryset().get(*args, **kwargs)
except self.model.MultipleObjectsReturned:
raise MultipleUniqueRecords()
except Exception as e:
logger.warning("SalesForce exception %s", str(e))
raise NoRecord()
return result
class SalesforceModel(with_metaclass(salesforce.models.SalesforceModelBase, models.Model)):
"""
Abstract model class for Salesforce objects.
"""
_base_manager = objects = SalesforceManager()
_salesforce_object = True
class Meta:
managed = False
abstract = True
Id = fields.SalesforceAutoField(primary_key=True)
def clean_fields(self, *args, **kwargs):
# Override the default clean_fields method so we can catch validation exceptions
try:
super(SalesforceModel, self).clean_fields(*args, **kwargs)
except Exception as validation_exception:
detail = ''
for field, message in validation_exception.error_dict.items():
detail += field + ': ' + message[0].messages[0]
raise ValidationError(detail)
def save(self, *args, **kwargs):
# Override the default save method so we can remove fields that Salesforce manages
if self._meta.model_name in ['contact', 'account', 'license', 'sslcertificate']:
if not self.Id:
for field in self._meta.fields:
if field.attname == 'created_date' or field.attname == 'certificate_id':
self._meta.fields.remove(field)
else:
update_fields = self._meta.get_all_field_names()
remove_list = []
if self._meta.model_name == 'contact':
remove_list = ['created_date', 'accesstoken', 'refreshtoken', 'oauth2_client', 'grant', 'Id', 'entitlement_plan']
elif self._meta.model_name == 'account':
remove_list = ['created_date', 'account', 'Id']
elif self._meta.model_name == 'license':
remove_list = ['created_date', 'Id']
elif self._meta.model_name == 'sslcertificate':
remove_list = ['certificate_id', 'created_date', 'Id']
for remove_field in remove_list:
if remove_field in update_fields:
update_fields.remove(remove_field)
kwargs['update_fields'] = update_fields
# Retry five times if there's a SalesforceError
delay = 1
for retry in range(5):
try:
super(SalesforceModel, self).save(*args, **kwargs)
break
except Exception as e:
logger.error("Saving {0} resulted in an error {1}, retry {2}".format(str(self),str(e),retry))
if retry < 4 and "SERVER_UNAVAILABLE" in str(e):
time.sleep(delay)
delay *= 2
else:
(此处评论占位较大,暂未回复。)
当我在您的应用程序代码中看到来自我的数据库后端的一行时,我以为我的眼睛在骗人,也许在同一模块中还有一个具体模型。我看到您粘贴的代码基于旧的 django-salesforce 0.5.x 代码。好的,希望你也使用包 0.5.x。它可能无法与当前的 django-salesforce 0.6 一起使用,或者没有人会期待它
最安全的样式是使用publicAPI。一种更脆弱的风格是也使用未记录的功能。最脆弱的是从其他包的核心复制粘贴代码而忘记你开始使用新版本。如果你这样做,你必须有足够严肃的理由和足够的时间。 (我为每个 Django 版本做了六次,因为 django-salesforce 后端对我来说足够有趣,但我不推荐给你。)
你可以简单的写:
class MySalesforceModel(salesforce.models.SalesforceModel):
def ... # your custom method
class Meta:
managed = False
abstract = True
优点是这将使用正确的当前 SalesforceModel,即使是下一个版本的 Django 和 django-salesforce。
如果您确实需要自定义后端,请将后端和应用程序代码合理分离到不同的模块中,以便在不导入任何具体模型的情况下导入后端。否则你很容易得到丑陋的依赖。此外,一个非空的 'init.py` 会导致愚蠢的依赖关系
如果你有完整的源代码,你不需要假设("hypo")这样简单的事情。您可以简单地记录(打印)该信息,例如放置 import sys; print("settings imported", "my_project.settings" in sys.modules)
或验证设置是否正确配置 - 在有问题的地方之前临时放置一行:from django.conf import settings; print(settings.INSTALLED_APPS) # any valid name
你应该在寻求帮助之前检查语法错误。对于子目录树的任何特定 Python 版本都可以轻松完成,例如通过 python2.7 -m compileall $(pwd)
使用通用处理 except Exception:
非常疯狂,尤其是在调试时。想象一下 try...except 之间的代码会引发 ImportError 而你吞下了它。
通过重构代码来解决依赖关系比以后维护脆弱的代码更容易。想象一下,如果您在一个函数中导入,并且只有在模块仅从 Apache 中的线程导入时,您才会从您的朋友那里收到一条奇怪的 ImportError 消息,但如果您从主线程导入它,它就会成功。支持显式导入。