django-simple-history
django-simple-history copied to clipboard
How to specify table name format in base model depending on child model's overriden table name ?
Hi, I have a Base model like this, which many models would inherit from,
class BaseModel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
last_updated_date = models.DateTimeField(auto_now=True)
history = HistoricalRecords(inherit=True)
I have many child models inheriting from this BaseModel and I have overriden the db table names to be with underscores. Eg.
class ChildModel(BaseModel):
title = models.CharField(max_length=255, blank=False, null=False)
class Meta:
db_table = "child_model"
I want History table to be created as appname_historical_child_model
. But right now its getting created as appname_historicalchildmodel
Any suggestions on how to achieve this?
@avinashs2401
If you manage it in the base model only, you can override the HistoricalRecords
class and specify a custom attribute DEFAULT_MODEL_NAME_PREFIX
.
Otherwise you can mention the history
field specifically each model with table_name
attribute.
@avinashs2401
As per my understanding, there is no way to specify custom table name for history models of inherited child models.
You can only change the history table name where HistoricalRecords
instance is created (i.e. in the Base Class).
Overriding db_table option of Meta will only update table name for the model itself ( this is done by django: Meta db_table option). For the corresponding, history model db table name will be derived from the history model name.
@muneeb706
You could override the HistoricalRecords
's create_history_model
method and add a logic using the model name being generated, to change the attrs["Meta"].db_table
as per your requirements
@singhravi1
Do you mean, something like this ?
from django.db import models
from simple_history.models import HistoricalRecords
class CustomHistoricalRecords(HistoricalRecords):
def create_history_model(self, model, inherited):
history_model = super().create_history_model(model, inherited)
history_model._meta.db_table = "historical_" + model._meta.db_table
return history_model
class BaseModel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
last_updated_date = models.DateTimeField(auto_now=True)
history = CustomHistoricalRecords(inherit=True)
class ChildModel(BaseModel):
title = models.CharField(max_length=255, blank=False, null=False)
class Meta:
db_table = "child_model"
@singhravi1
Do you mean, something like this ?
from django.db import models from simple_history.models import HistoricalRecords class CustomHistoricalRecords(HistoricalRecords): def create_history_model(self, model, inherited): history_model = super().create_history_model(model, inherited) history_model._meta.db_table = "historical_" + model._meta.db_table return history_model class BaseModel(models.Model): created_date = models.DateTimeField(auto_now_add=True) last_updated_date = models.DateTimeField(auto_now=True) history = CustomHistoricalRecords(inherit=True) class ChildModel(BaseModel): title = models.CharField(max_length=255, blank=False, null=False) class Meta: db_table = "child_model"
Please, note, the meta class db_table attribute will not be updated.
@singhravi1 Do you mean, something like this ?
from django.db import models from simple_history.models import HistoricalRecords class CustomHistoricalRecords(HistoricalRecords): def create_history_model(self, model, inherited): history_model = super().create_history_model(model, inherited) history_model._meta.db_table = "historical_" + model._meta.db_table return history_model class BaseModel(models.Model): created_date = models.DateTimeField(auto_now_add=True) last_updated_date = models.DateTimeField(auto_now=True) history = CustomHistoricalRecords(inherit=True) class ChildModel(BaseModel): title = models.CharField(max_length=255, blank=False, null=False) class Meta: db_table = "child_model"
Please, note, the meta class db_table attribute will not be updated.
I don't think we can access attrs attribute as it is defined inside the create_history_model method.
@muneeb706 I'm thinking of something like the following,
class CustomHistoricalRecords(HistoricalRecords):
def create_history_model(self, model, inherited):
"""
Creates a historical model to associate with the model provided.
"""
if not self.table_name:
self.table_name = f"{model._meta.app_label}_{re.sub(r'(?<!^)(?=[A-Z])', '_', self.get_history_model_name(model)).lower()}"
return super().create_history_model(model, inherited)
Or maybe you directly refer to the child model's db_table
Meta option you've specified using model._meta.db_table
.
I've not tested this but please confirm if it works.
@singhravi1
If you update self.table_name
then the db_table will be updated. However, in your code snippet, you have added the condition.
if not self.table_name
which will be True only when the table_name is not set in the base model HistoricalRecords instance.
However, you can make it work by updating self.table_name
property as in HistoricalRecords.create_history_model
method
attrs["Meta"].db_table = self.table_name
. This may not be the best way to do it, as according to current implementation self.table_name
is supposed to point to value that is set when HistoricalRecords instance is created (which is in base class in this scenario).
Hey @muneeb706 ,
The check can be changed based on the logic that mentioning table_name
with inherit=True
would not make sense.
Not the best way but till the time this feature is included in the package, users such as the author can extend it to have what they require.
The following updated code should work for the problem in hand,
class CustomHistoricalRecords(HistoricalRecords):
def create_history_model(self, model, inherited):
if self.inherit:
self.table_name = f"{model._meta.app_label}_{re.sub(r'(?<!^)(?=[A-Z])', '_', self.get_history_model_name(model)).lower()}"
return super().create_history_model(model, inherited)