How to revert the last migration? - python

I've made a migration that added a new table and want to revert it and delete the migration, without creating a new migration.
How do I do it? Is there a command to revert last migration and then I can simply delete the migration file?

You can revert by migrating to the previous migration.
For example, if your last two migrations are:
0010_previous_migration
0011_migration_to_revert
Then you would do:
./manage.py migrate my_app 0010_previous_migration
You don't actually need to use the full migration name, the number is enough, i.e.
./manage.py migrate my_app 0010
You can then delete migration 0011_migration_to_revert.
If you're using Django 1.8+, you can show the names of all the migrations with
./manage.py showmigrations my_app
To reverse all migrations for an app, you can run:
./manage.py migrate my_app zero

The answer by Alasdair covers the basics
Identify the migrations you want by ./manage.py showmigrations
migrate using the app name and the migration name
But it should be pointed out that not all migrations can be reversed. This happens if Django doesn't have a rule to do the reversal. For most changes that you automatically made migrations by ./manage.py makemigrations, the reversal will be possible. However, custom scripts will need to have both a forward and reverse written, as described in the example here:
https://docs.djangoproject.com/en/1.9/ref/migration-operations/
How to do a no-op reversal
If you had a RunPython operation, then maybe you just want to back out the migration without writing a logically rigorous reversal script. The following quick hack to the example from the docs (above link) allows this, leaving the database in the same state that it was after the migration was applied, even after reversing it.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def forwards_func(apps, schema_editor):
# We get the model from the versioned app registry;
# if we directly import it, it'll be the wrong version
Country = apps.get_model("myapp", "Country")
db_alias = schema_editor.connection.alias
Country.objects.using(db_alias).bulk_create([
Country(name="USA", code="us"),
Country(name="France", code="fr"),
])
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunPython(forwards_func, lambda apps, schema_editor: None),
]
This works for Django 1.8, 1.9
Update: A better way of writing this would be to replace lambda apps, schema_editor: None with migrations.RunPython.noop in the snippet above. These are both functionally the same thing. (credit to the comments)

Don't delete the migration file until after the reversion. I made this mistake and without the migration file, the database didn't know what things to remove.
python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}
If you want to revert all migrations, use zero as the name of the migration:
python manage.py migrate app_name_here zero
Delete the migration file. Once the desired migration is in your models...
python manage.py makemigrations
python manage.py migrate

Here is my solution, since the above solution do not really cover the use-case, when you use RunPython.
You can access the table via the ORM with
from django.db.migrations.recorder import MigrationRecorder
>>> MigrationRecorder.Migration.objects.all()
>>> MigrationRecorder.Migration.objects.latest('id')
Out[5]: <Migration: Migration 0050_auto_20170603_1814 for model>
>>> MigrationRecorder.Migration.objects.latest('id').delete()
Out[4]: (1, {u'migrations.Migration': 1})
So you can query the tables and delete those entries that are relevant for you. This way you can modify in detail. With RynPython migrations you also need to take care of the data that was added/changed/removed. The above example only displays, how you access the table via Djang ORM.

I did this in 1.9.1 (to delete the last or latest migration created):
rm <appname>/migrations/<migration #>*
example: rm myapp/migrations/0011*
logged into database and ran this SQL (postgres in this example)
delete from django_migrations where name like '0011%';
I was then able to create new migrations that started with the migration number that I had just deleted (in this case, 11).

To revert a migration:
python manage.py migrate <APP_NAME> <MIGRATION_NUMBER_PREFIX>
MIGRATION_NUMBER_PREFIX is the number prefix of the migration you want to revert to, for instance 0001 to go to 0001_initial.py migration. then you can delete that migration.
You can use zero as your migration number to revert all migrations of an app.

The other thing that you can do is delete the table created manually.
Along with that, you will have to delete that particular migration file. Also, you will have to delete that particular entry in the django-migrations table(probably the last one in your case) which correlates to that particular migration.

You can revert by migrating to the previous migration.
For example use below command:
./manage.py migrate example_app one_left_to_the_last_migration
then delete last_migration file.

If you are facing trouble while reverting back the migration, and somehow have messed it, you can perform fake migrations.
./manage.py migrate <name> --ignore-ghost-migrations --merge --fake
For django version < 1.7 this will create entry in south_migrationhistory table, you need to delete that entry.
Now you'll be able to revert back the migration easily.
PS: I was stuck for a lot of time and performing fake migration and then reverting back helped me out.

First: find the previous migrations of your app,
python manage.py showmigrations
eg:
bz
[X] 0001_initial
[ ] 0002_bazifff
If you want rollback 0002_bazifff migrations,
python manage.py migrate bz 0001_initial
If you want rollback 0001_initial Or all
python manage.py migrate bz zero
It seems can't rollback 0001 only

This answer is for similar cases if the top answer by Alasdair does not help. (E.g. if the unwanted migration is created soon again with every new migration or if it is in a bigger migration that can not be reverted or the table has been removed manually.)
...delete the migration, without creating a new migration?
TL;DR: You can delete a few last reverted (confused) migrations and make a new one after fixing models. You can also use other methods to configure it to not create a table by migrate command. The last migration must be created so that it match the current models.
Cases why anyone do not want to create a table for a Model that must exist:
A) No such table should exist in no database on no machine and no conditions
When: It is a base model created only for model inheritance of other model.
Solution: Set class Meta: abstract = True
B) The table is created rarely, by something else or manually in a special way.
Solution: Use class Meta: managed = False
The migration is created, but never used, only in tests. Migration file is important, otherwise database tests can't run, starting from reproducible initial state.
C) The table is used only on some machine (e.g. in development).
Solution: Move the model to a new application that is added to INSTALLED_APPS only under special conditions or use a conditional class Meta: managed = some_switch.
D) The project uses multiple databases in settings.DATABASES
Solution: Write a Database router with method allow_migrate in order to differentiate the databases where the table should be created and where not.
The migration is created in all cases A), B), C), D) with Django 1.9+ (and only in cases B, C, D with Django 1.8), but applied to the database only in appropriate cases or maybe never if required so. Migrations have been necessary for running tests since Django 1.8. The complete relevant current state is recorded by migrations even for models with managed=False in Django 1.9+ to be possible to create a ForeignKey between managed/unmanaged models or to can make the model managed=True later. (This question has been written at the time of Django 1.8. Everything here should be valid for versions between 1.8 to the current 2.2.)
If the last migration is (are) not easily revertible then it is possible to cautiously (after database backup) do a fake revert ./manage.py migrate --fake my_app 0010_previous_migration, delete the table manually.
If necessary, create a fixed migration from the fixed model and apply it without changing the database structure ./manage.py migrate --fake my_app 0011_fixed_migration.

there is a good library to use its called djagno-nomad, although not directly related to the question asked, thought of sharing this,
scenario: most of the time when switching to project, we feel like it should revert our changes that we did on this current branch, that's what exactly this library does, checkout below
https://pypi.org/project/django-nomad/

All the other answers work great for rolling back linear migrations. However, when the migration is non-linear i.e has multiple leaf nodes and we wish to rollback only one path, then we can do in following way:
X
/ \
A B [A and B can represent any number of linear migrations.]
\ /
Y (merge migration)
If we need to rollback A,B and Y. Then we can do the way other answer states.
i.e python manage.py migrate app X.
However, if need to only unapply one path i.e, rollback B and Y, perform following steps:
Unapply only Y. By doing python manage.py migrate app B (or A; both works).
Remove the migration files A and Y for time being from the project location.
Now unapply B, by doing python manage.py migrate app X.
Bring the migration files A and Y to the original location. Now, you can safely delete the unapplied migrations B and Y, if you wish to.
The gist is django can only rollback migrations if the files are present in the location. If you do not want to rollback a migration path (i.e A here), remove it from the project location while performing rollback.
More on this. https://stackoverflow.com/a/71231925/8663277

Related

Reordering Initial Migration operations

Every time I try to migrate my initial migration, right after makemigrations, I get errors like :
django.db.migrations.exceptions.InvalidBasesError: Cannot resolve bases for [<ModelState: 'Project.Class'>]
This can happen if you are inheriting models from an app with migrations (e.g. contrib.auth)
The reason I think this happens is because the order of model operations in the 0001_initial.py migration is incorrect. Operations with classes which inherit from others are added before their parents'. After I reorder the operations, it works: Process finished with exit code 0. Cool! But how do I make makemigrations work without doing this every time?
Thanks!
ps. I tried reordering the import order of my models in the model's __init__.py but it didn't work.
If you have several apps in your Django project and in models of one app models of another app are referenced - this may result in such conflict.
It is recommended to create migrations for each app separately and reference another app migration as a dependency in referencing migration file.
python manage.py makemigrations app-one
python manage.py makemigrations app-two
# example of referencing dependent migration,
# so app-two 0001 migrations runs after app-one 0001 migration
# app-two/migrations/0001-initial.py
dependencies = [("app-one", "0001-init.py")]

How to use Django with an existing database in MySQL?

I have an application in Django 2.0 and as a database engine I use MySQL. I have a problem because the database was previously created and already has records, my idea is to use this same database for the application I am creating.
Use the command
python manage.py inspectdb > models.py
To create the models.py file which will be cleaned as indicated by the models.py file that was generated.
#This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey has `on_delete` set to the desired behavior.
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
After this I proceed to execute:
python manage.py migrate
python manage.py makemigrations
python manage.py migrate
But it generates the following error:
(1050, "Table 'XXXXXXX' already exists")
Obviously it tells me that the table already exists, but how do I not generate this error and continue administering the tables from Django.
You need to run --fake-initial or --fake. See more at Django migrations. Be careful because running inspectdb doesn't solve all your problems. You need to fix the things inside models.py manually and migrate again.
One of the things (and the main reason I do not use Django) is it likes to take control of everything. The fact that it controls the database means that if you don't start strictly in Django, you are doing it wrong.
However there is a work around:
https://docs.djangoproject.com/en/2.0/howto/legacy-databases/

django: exclude models from migrations

In my django application (django 1.8) I'm using two databases, one 'default' which is MySQL, and another one which is a schemaless, read-only database.
I've two models which are accessing this database, and I'd like to exclude these two models permanently from data and schema migrations:
makemigrations should never detect any changes, and create migrations for them
migrate should never complain about missing migrations for that app
So far, I've tried different things, all without any success:
used the managed=False Meta option on both Models
added a allow_migrate method to my router which returns False for both models
Does anyone have an example of how this scenario can be achieved?
Thanks for your help!
So far, I've tried different things, all without any success:
used the managed=False Meta option on both Models
That option (the managed = False attribute on the model's meta options) seems to meet the requirements.
If not, you'll need to expand the question to say exactly what is special about your model that managed = False doesn't do the job.
I thought, I have a problem with makemigrations. It pretends to make migration on managed = False model, but no SQL code generated for this model
Here is my example, model Smdocumets unmanaged, and no SQL code was generated.
python manage.py makemigrations
Migrations for 'monitor':
monitor\migrations\0005_auto_20171102_1125.py
- Create model Smdocuments
- Add field sid to db
- Alter field name on db
python manage.py sqlmigrate monitor 0005
BEGIN;
--
-- Create model Smdocuments
--
--
-- Add field sid to db
--
ALTER TABLE "monitor_db" RENAME TO "monitor_db__old";
...
You have the correct solution:
used the managed=False Meta option on both Models
It may appear that it is not working but it is likely that you are incorrectly preempting the final result when you see - Create model xxx for models with managed = False when running makemigrations.
How have you been checking/confirming that migrations are being made?
makemigrations will still print to terminal - Create model xxx and create code in the migration file but those migrations will not actually result in any SQL code or appear in Running migrations: when you run migrate.

Adding Columns to Existing Schema

I am just getting started with Django (1.8) and I am a bit confused about how to modify my models.
If I go in and add a new field to an existing model, I start getting "No Such Column" error. So far, I've just been wiping my DB and starting over, but that gets annoying, is there a process for this?
What happens when I go to production? How would I modify the schema at that point? All the resources I see online are for South, which I guess is built into this version of django, but still can't find any solid info.
Thanks
In django 1.7+ there is no need of south.Only
python manage.py makemigrations
python manage.py migrate
If you're changing over from an existing app you made in django 1.7-, then you need to do one pre-step (as I found out) listed in the documentation:
python manage.py makemigrations your_app_label
Also try this
class Mymodel(models.Model):
myfiled = models.CharField()
# ...
class Meta:
managed = True
You can write you own sql command to add the missing column giving you that error.
first run:
python manage.py makemigrations --empty yourappname
then you can go to your app and find the newly generated migration file
having a code similar to the code below.
I'm not sure the kind of column you want add but inside operations in the below code, you put your sql command. In my example I'm just adding a character varying field.
class Migration(migrations.Migration):
dependencies = [
('app_name', '0002_auto_20150619_2439'),
]
operations = [
migrations.RunSQL('ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL')
]
I hope this helps.

Django 1.7.1 requires a Default value for field - but no entry is in database. Why?

I ran into a strange problem. I'm using Django 1.7.1 on Mac OS X Yosemite and I have configured a local MySQL database.
Normally, I create a model and if I want to add another field, I just do a ./manage.py migrate and Django creates a new database column.
I did exactly that. Here's my model:
from django.db import models
from django.utils.translation import ugettext as _
class Product(models.Model):
CATEGORY_CHOICES = (
('apphosting', _('Application Hosting')),
('webhosting', _('Web Hosting'))
)
category = models.CharField(max_length=25, choices=CATEGORY_CHOICES)
name = models.CharField(max_length=25)
price_monthly = models.FloatField()
def __unicode__(self):
return self.name
Please note that I have added the field price_monthly. Then, I did a ./manage.py migrate:
(mysphere)dmanser#ragamuffin:~/git/mysphere on master [!?]$ ./manage.py migrate
Operations to perform:
Synchronize unmigrated apps: crispy_forms
Apply all migrations: customers, sessions, admin, sites, flatpages, contenttypes, products, auth
Synchronizing apps without migrations:
Creating tables...
Installing custom SQL...
Installing indexes...
Running migrations:
No migrations to apply.
Your models have changes that are not yet reflected in a migration, and so won't be applied.
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
So ok, I do a ./manage.py makemigrations, which results in:
(mysphere)dmanser#ragamuffin:~/git/mysphere on master [!?]$ ./manage.py makemigrations
You are trying to add a non-nullable field 'price_monthly' to product without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Select an option:
The strange thing here is, that this model has no entry yet. So why should I need to provide a default value if there's no product in the database?
I'm starting to pull my hair out and I have tried several things yesterday. After 3 hours, I gave up.
The migrations system is designed so that a single migration can be applied to more than one database. For example, you could have a development version, a staging version, and one or more production versions. That's why making the migration is a distinct step from applying the migration, and why makemgirations can't just look at the currently active database to see that it doesn't have any rows. What if you then try to apply the migration to a database that does?
The solution in your case is simple: since there are no rows, option 1 (setting a default on all existing rows) won't do anything at all. So choose option 1, and any value you like.
You have already applied migrations before, so the table has already been created. Now you are adding a column that cannot be null, but have not defined a default value.
Since migrations will add a column to an existing table, it needs enough data so that your migration does not violate the schema; hence the prompt.
If you were to delete the table, and then run the migration you would not face this error. However as the initial migration has already created the table, any future migrations cannot not violate its referential integrity.
I have come across this many times, suppose you have app called app1 inside your project myproject, best thing you can do in this situation is to go and delete app1/migrations then try again it should work fine

Categories

Resources