Using clean() in your Django models post migration from a legacy stack
If you're migrating an application from another stack where foreign keys were not enforced in the database and the some of the rows' foreign key fields have values as 0, then these would cause issues in the new application where the framework has strict checks and would throw an exception for 0 being saved as a foreign key value.
Let's take an example model from Django docs. https://docs.djangoproject.com/en/5.0/topics/db/examples/many_to_one/
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
media_house = models.ForeignKey(MediaHouse, on_delete=models.CASCADE, null=True, blank=True) # Added, NEW
some_other_model = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE, null=True, blank=True) # Added, NEW
Let's say you have a row with primary key 915 and it contains media_house_id
as 0 and some_other_model_id
also as 0. Now let's just try to save this just after retrieving the row.
try:
article = Article.objects.get(pk=915)
article.save()
except Exception as e:
msg = str(e)
You would get an exception.
The database backend does not accept 0 as a value for AutoField.
This is because the row with primary key ID 915 has an entry of media_house_id
equal to 0 and/or some_other_model_id
equal to 0 for which Foreign Key constraint fails as there isn't an entry (row) in the MediaHouse and SomeOtherModel tables where the primary key for those tables are 0.
So instead, we call the clean
method (for which we write a clean
function in the model) to set the field to None if it's 0.
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
media_house = models.ForeignKey(MediaHouse, on_delete=models.CASCADE, null=True, blank=True) # Added, NEW
some_other_model = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE, null=True, blank=True) # Added, NEW
def clean(self):
if self.media_house_id == 0:
self.media_house_id = None
if self.some_other_model_id == 0:
self.some_other_model_id = None
And before calling the save()
method, we just call clean()
as well.
try:
article = Article.objects.get(pk=915)
article.clean() # NEW : Call clean() to convert fields' invalid values to valid values or None (Null in MySQL)
article.save()
except Exception as e:
msg = str(e)