正确地导入Django管理命令

8 浏览
0 Comments

正确地导入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, in 
    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 
    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, in 
    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 
    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, in 
    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):
    """
    重写默认的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:

0
0 Comments

问题原因:在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消息,但如果你从主线程导入它则成功。显式导入是首选。

0