I'm developing a Django application on Google Appengine, and I came across a db design problem where multi-table inheritance seems to be the best solution. Unfortunately, however, multi-table inheritance is not supported on Appengine because it requires JOINs. I'm looking for an alternative solution that meets the following requirements:
** See updates at bottom **
There are 3 different user types, or profiles:
- Business (i.e. owner)
- Employee
- Client
These profiles share certain attributes, but also have unique attributes for their respective types. For example, all profiles have a contact email and phone number, but only Businesses need to provide a logo or specify their business type.
In addition, I need to be able to retrieve Profile objects from the db (regardless of type), and get the extended or child profile for each (Business, Employee, or Client). Also, a Business, Employee, or Client object should also be able to access the parent profile easily. In other words, the relationship needs to work in both direction (like profile.employee
or employee.profile
).
So far, I have come up with two possible solutions:
OneToOneField in child model:
class Profile(models.Model):
# Profile may exist before user claims it
user = models.OneToOneField(User, blank=True, null=True)
email ...
phone ...
... other common fields ...
class Business(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="biz_profile")
class Employee(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="employee_profile")
class Client(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="client_profile")
This will allow me to do the following: profile.biz_profile
and biz.profile
Unique Generic Foreign Key in parent model:
class Profile(models.Model):
content_type=models.ForeignKey(ContentType)
object_id=models.PositiveIntegerField()
content_object=generic.GenericForeignKey('content_type','object_id')
email ...
phone ...
... other common fields ...
class Meta:
unique_together = ('content_type', 'object_id')
class Business(models.Model):
profiles = generic.GenericRelation(Profile)
class Employee(models.Model):
profiles = generic.GenericRelation(Profile)
class Client(models.Model):
profiles = generic.GenericRelation(Profile)
This will allow me to do the following: profile.content_object
and biz.profiles.all()[0]
The first approach (OneToOneField) seems the most straight forward, but I would need to come up with a better way to know which child to call, perhaps by setting the content_type in the Profile model creating a method like:
def get_instance(self):
# Need to look at contenttype framework, but you get the idea
if self.content_type == 'business':
return self.biz_profile
elif self.content_type == 'employee':
return self.employee_profile
elif self.content_type == 'client':
return self.client_profile
return None
I'm not set on either of these solutions, so I welcome any alternative solutions or improvements to what I have here.
Thanks in advance!
UPDATE
My original requirements have changed since I first posted. It turns out I only need parent>child access NOT child>parent. In light of this, I'm going to use the unique Generic Foreign Key approach instead. However, I am still looking for an answer to the original question, so don't be shy if you have a solution.