Django - Alternative to multi-table inheritance in Appengine
Asked Answered
C

0

2

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:

  1. Business (i.e. owner)
  2. Employee
  3. 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.

Calcifuge answered 21/6, 2011 at 20:26 Comment(4)
Why not use PolyModel? e.g. class Profile(polymodel.PolyModel), class Business(Profile), class Employee(Profile) etc...Anglocatholic
Thanks for your response. I was not aware of PolyModel. Is it compatible with Django models and model fields? I'd prefer to keep my project strictly Django (via djangoappengine) if possible, but I'll definitely look into it.Calcifuge
Sorry I am not sure, there is some discussion here: #3141274Anglocatholic
No, polymodel is part of App Engine's DB library. @godshall is using Django models.Fuscous

© 2022 - 2024 — McMap. All rights reserved.