Migrations reflect not only database but some business logic. Migrations blow up
Let us suppose that we had a model (example is from the documentation https://docs.djangoproject.com/en/4.1/ref/models/fields/#filefield):
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
Migrations will be something like:
('upload', models.FileField(upload_to=vocabulary_phrases.models.user_directory_path)),
But now you decide to change to a class:
class UploadTo:
def __init__(self, folder, filename_suffix=""):
self.folder = folder
self.filename_suffix = filename_suffix
def _get_filename(self, instance, filename):
_, file_extension = os.path.splitext(filename)
result = str(instance.a_uuid)
if self.filename_suffix:
result += "-{}".format(self.filename_suffix)
result += file_extension
return result
def save_path(self, instance, filename):
tmp_filename = self._get_filename(instance, filename)
result = "{}/{}".format(self.folder.value,
tmp_filename)
return result
class MyModel(models.Model):
upload = UploadTo(folder=UPLOAD_TO.VOCABULARY_FILE_FOLDER, filename_suffix="trololo").save_path
When you try to makemigrations, this code will blow. It will complain like 0002_migration contains user_directory_path, it is absent.
This change in the code not mechanical. I can't imagine that it can be done just by refactor / rename in IDE. Then I can't imagine what the new migration should look like. This means that I will not be able to modify migrations file easily.
I will have to deploy another project next to this one and another database. Delete all migrations, makemigrations, copy created migration and substitute all occurrances of user_directory_path with what is in my clipboard.
Complicated, I would say. And I suspect then it may be error prone.
This was just one example. Django hardcodes something in migrations which I would say is related more to business logics than to the database itself. Let us not discuss whether this example is good or not. It illustrates how something from our code is hardcoded in migrations and Django fails automatically make new migrations.
Could you recommend me what is the best practice of coping with this problem?
A simple solution would be to run
python manage.py makemigrations
while you still have user_directory_path
in your file
eg:
import os
from django.db import models
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return "user_{0}/{1}".format(instance.user.id, filename)
class UploadTo:
def __init__(self, folder, filename_suffix=""):
self.folder = folder
self.filename_suffix = filename_suffix
def _get_filename(self, instance, filename):
_, file_extension = os.path.splitext(filename)
result = str(instance.a_uuid)
if self.filename_suffix:
result += "-{}".format(self.filename_suffix)
result += file_extension
return result
def save_path(self, instance, filename):
tmp_filename = self._get_filename(instance, filename)
result = "{}/{}".format(self.folder.value, tmp_filename)
return result
class UPLOAD_TO:
VOCABULARY_FILE_FOLDER = "asd"
class MyModel(models.Model):
upload = UploadTo(
folder=UPLOAD_TO.VOCABULARY_FILE_FOLDER, filename_suffix="trololo"
).save_path
then you can squash migrations and delete the function which will avoid the need to deploy a copy project