I’ve created a series of custom ModelFields that are simply restricted ForeignKeys. Below you’ll find CompanyField. When instantiated, you may provide a type (e.g., Client, Vendor). With a type provided, the field ensures that only values that have the appropriate type are allowed.
The crm app, the one that defines the custom fields, compiles and runs just fine. Eventually I added references to the fields to a different app (incidents) using “from crm import fields”. Now I’m seeing a whole bunch of errors like this:
incidents.incident: ‘group’ has a relation with model Company, which has either not been installed or is abstract.
Here are all the gory details. Please let me know if there’s any more information I could provide which may be helpful.
## 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")
The was fixed, after much toil, simply by changing
to
It seems that when the ‘to’ argument was evaluated outside of the crm app, it was evaluating it in the context of the incidents app. That is, it was looking for ‘incidents.Company’ which, as the error message suggested, didn’t exist.