The process of removing a table is similar to the one of removing a column, and it consists of the same steps that depend on CD features in each case:
1. Performing post-deployment migrations to all pods in one deployment.
2. Performing migrations with the second deployment immediately after the first one in which the code was deployed.
3. Performing migrations with a separate deployment after deploying the code, but with a possible delay and even other deployments.
In previous articles (A Beginner's Guide to Deploying Django Model Changes to Production and Deleting a Column from a Django Model on Production), I described the problem of seamless deployment of applications written in Django, namely the break of code consistency and database structure when performing migrations before changing the code, and the process of alternate changes to the code in each container on production. There is one more simple case of deployment to production in a few steps. This is table/model removal.
Removing a Table on Production
The process of removing a table is similar to the one of removing a column, and it consists of the same steps that depend on features in each case:
Performing post-deployment migrations to all pods in one deployment.
Performing migrations with the second deployment immediately after the first one in which the code was deployed.
Performing migrations with a separate deployment after deploying the code, but with a possible delay and even other deployments.
Migration at the End
If migrations are performed after the code is deployed, then the following will happen:
The whole code is replaced on production. The new code does not contain any requests to the table that are being deleted because the model has been deleted. However, the table is still in the database. After the code has been completely replaced with the new one, migration is performed during which the already-unused table is deleted. It appears that during deployment, there is no chance that something can break due to model removal. With such a CD, nothing else needs to be done.
Migration After Code
This case suggests performing a migration before deploying the code. The important point is that it is possible to perform two deployments sequentially one right after another. In this case, the standard preparation for removing a model with a division into two deployments is sufficient. The first deployment is the removal of a model in the code. The second one contains the migration file, which will be executed as a result of the deployment.
Migration after Code with Delay
Sometimes you do not control deployments. This is a rare situation, but it happens. For example, you prepare merge requests and make a request for deployment. Meanwhile, requests are executed in turns that you do not control. That is, a considerable period of time can pass between merging to the master branch and deployments.
And, just like in the previous case, you need to deploy the code to production first, and then perform the migration.
It does not seem to be a big problem if migration is performed much later than the deployment of the code, but not always.
Sometimes your code is merged to the master branch but your migration is not there yet. Your colleague does not know that and creates their own migration with the following command:
python manage.py makemigrations
Their migration is joined by the removal of the table whose model you have already deleted. This is not surprising because their code no longer has a model, but it also doesn’t have any migration yet. You are lucky if your colleague is careful and your team has a code review, and everything is reliable. Then they can manually remove the code or merge it to their new master or your branch.
However, the situation can be different, and then you are the one responsible for your unfinished deployment. In this case, you can use a scheme in which the second deployment with migration may not depend on what happens in the interval and may not affect it. In fact, this scheme is a little more reliable than the previous one.
Then you need to do everything you did in the previous step, but tell Django that you do not need to add a migration to delete the table. To do this, a fake migration is performed in the first step, which, as in the case of removing the column, does not remove the table. With the second deployment, you will need to delete the table using raw SQL.
Example
The simplest model
class MyModel(models.Model):
name = models.CharField(max_length=100)
If you perform this migration before the code is replaced, then the request to the model will cause an error.
The first and the second cases end here. According to the second scenario, first, you need to deploy the code for removing a model, and then perform the migration. The first case suggests both changes in one deployment.
For the third case, you replace the migration code with:
Here, using state_operations, we have informed Django that we will do everything ourselves in the database, and it is only needed to write that the removed table is no longer there. This migration will not change anything in the database, that’s why it is deployed along with the code.
Now, we do not have to worry that before a table is deleted, someone will face the inconsistency of the database and code or will have to resolve other people’s migrations in their code.
The second migration will contain the following code:
Even if the second deployment containing this migration takes a very long time and before it is finished, someone updates their code to the master branch, nothing bad will happen.
However, the latter option is rarely used. Firstly, it's complex. Secondly, it rarely happens that deploying several versions in a row can cause a problem. Thirdly, you can always ask your team to be careful as an inconsistent release is being deployed. Therefore, the second approach is the most common.