Python 引用同一应用程序中模型的自定义模型字段的导入问题

Python 引用同一应用程序中模型的自定义模型字段的导入问题,python,Python,我创建了一系列自定义模型字段,这些字段只是受限制的外键。在下面你会发现CompanyField。实例化时,您可以提供一个类型(例如,客户、供应商)。提供类型后,该字段确保只允许具有适当类型的值 crm应用程序定义了自定义字段,编译和运行都很好。最后,我使用“from crm import fields”将字段引用添加到不同的应用程序(事件)。现在我看到了一大堆这样的错误: 事件。事件:“组”与模型公司有关系,模型公司未安装或是抽象的 这些都是血淋淋的细节。请让我知道,如果有任何更多的信息,我可以

我创建了一系列自定义模型字段,这些字段只是受限制的外键。在下面你会发现CompanyField。实例化时,您可以提供一个类型(例如,客户、供应商)。提供类型后,该字段确保只允许具有适当类型的值

crm应用程序定义了自定义字段,编译和运行都很好。最后,我使用“from crm import fields”将字段引用添加到不同的应用程序(事件)。现在我看到了一大堆这样的错误:

事件。事件:“组”与模型公司有关系,模型公司未安装或是抽象的

这些都是血淋淋的细节。请让我知道,如果有任何更多的信息,我可以提供这可能是有益的

## crm/fields.py

import models as crmmods

class CompanyField(models.ForeignKey):
    def __init__(self, *args, **kwargs):
        # This is a hack to get South working. In either case, we just need to
        # make sure the FK refers to Company.
        try:
            # kwargs['to'] == crmmods.company doesn't work for some reason I
            # still haven't figured out
            if str(kwargs['to']) != str(crmmods.Company):
                raise AttributeError("Only crm.models.Company is accepted " + \
                    "for keyword argument 'to'")
        except:
            kwargs['to'] = 'Company'

        # See if a CompanyType was provided and, if so, store it as self.type
        if len(args) > 0:
            company_type = args[0]
            # Type is expected to be a string or CompanyType
            if isinstance(company_type, str):
                company_type = company_type.upper()
                if hasattr(crmmods.CompanyType, company_type):
                    company_type = getattr(crmmods.CompanyType, company_type)
                else:
                    raise AttributeError(
                        "%s is not a valid CompanyType." % company_type)
            elif not isinstance(company_type, crmmods.CompanyType):
                raise AttributeError(
                    "Expected str or CompanyType for first argument.")

            self.type = company_type
        else:
            self.type = None

        super(CompanyField, self).__init__(**kwargs)

    def formfield(self, **kwargs):
        # Restrict the formfield so it only displays Companies with the correct
        # type.
        if self.type:
            kwargs['queryset'] = \
                crmmods.Company.objects.filter(companytype__role=self.type)
        return super(CompanyField, self).formfield(**kwargs)

    def validate(self, value, model_instance):
        super(CompanyField, self).validate(value, model_instance)

        # No type set, nothing to check.
        if not value or not self.type:
            return

        # Ensure that value is correct type.
        if not \
        value.companytype_set.filter(role=self.type).exists():
            raise ValidationError("Company does not have the " + \
                "required roles.")

## crm/models.py

import fields

class CompanyType(models.Model):
    name = models.CharField(max_length=25)

class Company(models.Model):
    type = models.ForeignKey(CompanyType)

class Person(models.Model):
    name = models.CharField(max_length=50)
    company = fields.CompanyField("Client")

## incidents/models.py

from crm import fields as crmfields

class Incident(models.Model):
    company = crmfields.CompanyField("Client")

您有一个循环包依赖项<代码>字段导入<代码>模型哪些导入<代码>字段哪些导入<代码>模型哪些导入<代码>字段

循环包依赖是一个坏主意(tm)。虽然它在某些情况下可能会起作用,但在您的情况下它不起作用,因为涉及元类的复杂原因,对此我将不多说

编辑

原因是Django ORM模块使用元类将其类变量(模型的字段)转换为对象上的属性描述符。这是在类定义时由元类完成的。类是在加载模块时定义的。因此,它的属性也必须在类加载时定义。这与方法的代码不同,在方法中,对名称的引用在类执行时就被解析

现在,由于您在类定义中从
模型中引用
字段
对象,然后再返回,因此这将不起作用


如果你把这三个放在同一个包里,你的问题就会解决

经过许多努力,这个问题只需简单地改变一下就解决了

kwargs['to'] = 'Company'


似乎在crm应用程序之外评估“to”参数时,它是在事件应用程序的上下文中评估的。也就是说,它正在查找“Events.Company”,正如错误消息所示,它并不存在。

谢谢Hans。我知道循环依赖是个坏主意。我认为这是一个罕见的情况下,它是必要的,因为我不认为这些文件都属于同一个包。你能给我链接一些关于元类问题的信息吗?Josh,我已经更新了我的答案来解释元类。我不想在循环依赖的问题上“政治正确”,我担心这在你的情况下根本不起作用。这有点像是在一个漂亮干净的设计和一个丑陋凌乱的设计之间做出选择。生活充满了艰难的选择;-)虽然在这种情况下,我不认为把这三个包合并成一个包是丑陋或混乱的。乔希,这方面有什么消息吗?我很好奇你是否能解决这个问题。嘿,汉斯,谢谢你的登记。事实证明这个问题其实很小。看看我的答案。我对你的答案投了赞成票,因为信息仍然可靠。
kwargs['to'] = 'crm.Company'