正确地导入Django管理命令
正确地导入Django管理命令
自定义管理命令oauth.py需要从另一个模块中获取一个模型。当我包含"from appname.authentication.models import Contact"时,我得到"AttributeError: 'module' object has no attribute 'models'." - 在我能够构建一个测试套件来帮助升级之前,我被困在django 1.6上。
我应该如何正确地导入Contact?
其他值得注意的SO答案:
除/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 = '创建OAUTH用户并获取访问令牌。' option_list = BaseCommand.option_list + ( make_option('--create-client', dest='create_client', help='''返回元组...'''), make_option('--get-access-token', dest='get_access_token', help='''返回有时间限制的访问令牌...'''), ) 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, inexecute_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 from wowza.authentication.models import Contact File "/app/wowza/authentication/models.py", line 80, in class SalesforceModel(with_metaclass(salesforce.models.SalesforceModelBase, models.Model)): AttributeError: 'module' object has no attribute 'models'
假设 - 设置未被导入
因此,我的设置必须像在manage.py shell使用时一样被设置,因为如果我在文件顶部包含:
from django.conf import settings
settings.configure()
我得到:
Traceback (most recent call last): File "manage.py", line 10, inexecute_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 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.
假设 - 更深的语法错误(这应该会破坏生产)
在我的应用程序文件中搜索models.model的出现次数会得到四个结果,每个都具有正确的models.Model的大小写。
假设 - Contact已经被导入
当我注释掉导入并运行命令时,我得到:
Traceback (most recent call last): File "manage.py", line 10, inexecute_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): """ 重写默认的Salesforce管理器,以便我们可以获得一些合适的REST框架异常 """ 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)): """ Salesforce对象的抽象模型类。 """ _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): # 覆盖默认的clean_fields方法,以便我们可以捕获验证异常 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): # 覆盖默认的save方法,以便我们可以删除Salesforce管理的字段 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 # 如果出现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管理命令中正确导入的问题出现的原因是,代码中存在对于旧版本django-salesforce的依赖,而没有适应当前版本的更新。
解决方法:使用公共API是最安全的方式。更脆弱的方式是使用未记录的功能。最脆弱的方式是复制其他包的核心代码,并忘记你开始使用了新版本。如果你这样做,你必须有足够严重的原因和足够的时间。你可以简单地编写以下代码:
class MySalesforceModel(salesforce.models.SalesforceModel): def ... # your custom method class Meta: managed = False abstract = True
这样做的优势是,即使在下一个版本的Django和django-salesforce中,它也会使用正确的当前SalesforceModel。
如果你真的需要自定义后端,请合理地将后端和应用程序代码分离到不同的模块中,以便可以在不导入任何具体模型的情况下导入后端。否则,你可能会出现丑陋的依赖关系。同时,一个非空的`__init__.py`可能会导致愚蠢的依赖关系。
当你拥有完整的源代码时,对于这些简单的问题,你无需进行假设。你可以简单地记录(打印)这些信息,例如使用以下代码:
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消息,但如果你从主线程导入它则成功。显式导入是首选。