3

I want to have an abstract Company model in Django, and extend it depending on the type of the company involved:

class Company(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)

    class Meta:
        abstract = True

class Buyer(Company):
    # Buyer fields..
    pass

class Seller(Company):
    # Seller fields...
    pass

Every user on the system is associated with a company, so I want to add the following to the User profile:

company = models.ForeignKey('Company')

But this gives the dreaded error:

main.Profile.company: (fields.E300) Field defines a relation with model 'Company', which is either not installed, or is abstract.

So I imagine what I'm trying to do cannot be done. I saw that the contenttypes framework could be used for this purpose, as answered in this question. My issue with that is that I don't want the company field to point to any model, but just subclasses of the Company model.

Is there anything else I can use for this purpose?

Community
  • 1
  • 1
user1496984
  • 10,957
  • 8
  • 37
  • 46

1 Answers1

3

The reason the ForeignKey cannot reference an abstract model directly is that individual models that inherit from the abstract model actually have their own tables in the database.

Foreign keys are simply integers referencing the id from the related table, so ambiguity would be created if a foreign key was related to an abstract model. For example there might be be a Buyer and Seller instance each with an id of 1, and the manager would not know which one to load.

Using a generic relation solves this problem by also remembering which model you are talking about in the relationship.

It does not require any additional models, it simply uses one extra column.

Example -

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Foo(models.Model):
  company_type = models.ForeignKey(ContentType)
  company_id = models.PositiveIntegerField()
  company = GenericForeignKey('company_type', 'company_id')

And then -

>>> seller = Seller.objects.create()
>>> buyer = Buyer.objects.create()
>>> 
>>> foo1 = Foo.objects.create(company = seller) 
>>> foo2 = Foo.objects.create(company = buyer) 
>>> 
>>> foo1.company 
<Seller: Seller object>
>>> foo2.company 
<Buyer: Buyer object>
C S
  • 1,363
  • 1
  • 17
  • 26
metahamza
  • 1,405
  • 10
  • 26
  • 1
    Thanks for the answer! In your example, `company` can refer to *any* models, right? I could also make it point to the User model, for instance? I would have rather specified somewhere that it should only point to subclasses of Company, so that Django didn't let me reference a User. But I guess this could be done as a validation step? – user1496984 May 31 '15 at 17:26
  • 2
    Absolutely, check out [this answer](http://stackoverflow.com/questions/6335986/how-can-i-restrict-djangos-genericforeignkey-to-a-list-of-models). Basically you pass the `limit_choices_to` argument in to the `ForeignKey`. – metahamza May 31 '15 at 17:54