There's plenty of document and discussion for having multiple models share fields in a parent class/table using "concrete" or "multi-table" inheritance:
class Place(models.Model):
...
class Restaurant(Place):
...
However, I can't find much on the inverse use-case: splitting the fields of a single model across multiple tables to save the cost of loading wide columns except when you actually need them.
Consider the following scenario:
class Person_Profile(models.Model):
...lots of fields...
class Person_Preferences(models.Model):
...lots of fields...
class Person(Person_Profile, Person_Preferences):
...small set of core fields...
What works:
person.some_field
)The only thing I'm missing is how to elegantly control when Django loads the fields from the parent table, since currently, loading a Person with p = Person.objects.first()
results in all three tables being joined and selected by default.
I went looking for how Django let's you express selecting a subset of fields and arrived at the QuerySet methods defer() and only(), along with the suggestion there about using an unmanaged model as a facade to conveniently load a subset of fields:
class Person(models.Model):
name = models.CharField(max_length=30)
pref1 = models.BooleanField(default=False)
profile1 = models.CharField(max_length=30, null=True)
class SkinnyPerson(models.Model):
name = models.CharField(max_length=30)
class Meta:
managed = False
db_table = 'myapp_person'
Haven't reach a conclusion yet, but this method has a lot of appeal right now.
Splitted model - This is a good solution only if you don't understand the DataManagers/Queryset idea.
The splitting fields on the models is good approach, if you use Meta.abstract = True
. if you create two real сlasses only to add together all fields in child class - you loose many time with joins on every ask in DB.
more here: https://docs.djangoproject.com/en/4.1/topics/db/models/#abstract-base-classes
In QuerySet
you can define many methods. After that you can chain methods together.
class Person_Profile(models.Model):
Meta:
abstract = True
...lots of fields...
class Person_Preferences(models.Model):
Meta:
abstract = True
...lots of fields...
class Person(Person_Profile, Person_Preferences):
...small set of core fields...
objects = PersonQueryset.as_manager() # in old django PersonDataManager()
How might PersonDataManager
or PersonQuerySet
look like:
class PersonQuerySet(QuerySet):
def onlySpecialFields(self, *args, **kwargs):
return self.only(*my_special_only_list)
def deferSpecialFields(self, *args, **kwargs):
return self.defer(*my_special_defer_list)
def skinnyPersons(self, *args, **kwargs):
return self.only('name')
How you can use it:
SkinnyPersons_List = Person.objects.filter(name=something).skinnyPersons()
# after that
FirstSkinnyPerson = SkinnyPersons_List.first()
# somethere in code
LastSkinnyPerson = Person.objects.skinnyPersons().last()
Pros:
cons: