Django migrations reference a deleted module - django

I have a Model named FooModel defined in a my_app/models/foo.py.
After deleting foo.py, running Django (1.7) migrations raises an error since the old migration files import foo.py (import myapp.models.foo.FooModel).
How should I resolve this?
This happens when the model has an ImageField with an upload_to parameter.

There are two cases:
You moved FooModel elsewhere, then edit all your migration files to reflect that move.
You removed FooModel, in this case, follow these steps:
Put FooModel back to where it was.
Make sure there are no references to FooModel elsewhere in your code.
Run ./manage.py makemigrations my_app
Run ./manage.py squashmigrations my_app <migration created with the previous comand> — see the doc for more informations on squashing migrations.
Repeat the two previous steps for any app that references FooModel in its migrations.
Remove FooModel and the stale migration files once you ensured everything worked fine.
This should work because as FooModel is not referenced from any other model, it should be removed from the migrations files when squashing them.
However, be warned that squashing migrations is not a simple operation and may have consequences it might be a better idea to just keep the model in your codebase without using it.
Note: in this situation, the object in question is a Django model but this applies to any class, function or module referenced by migration files.

In custom database migrations you should not import your models directly because you can face this particular issue in the future. Instead you should rather use Djangos get_model function.
MyModel = apps.get_model('myapp', 'MyModel')
for row in MyModel.objects.all():
row.uuid = uuid.uuid4()
row.save(update_fields=['uuid'])
In this case the migrations will also run when you decide to delete the model in the future.
Further read: https://docs.djangoproject.com/en/2.2/howto/writing-migrations/

Related

Django Migrations: How to mark a migration as a parent of other apps migrations?

I'm trying to create a new Django application whose migrations depend on other apps migrations. I want migrations on the other apps to have a dependency on migrations on the new application, even when the new application doesn't reference at all the models at the other apps.
For example, let's say I have application A (an existing app) and application B (my new application):
A has a migration called A.0001, and B has a migration called B.0001 which depends on A.0001. I now make a change at A.MyModel, so I need to run python manage.py makemigrations to generate a new migration A.0002.
What I want is A.0002 to automatically depend on B.0001.
How can I specify this dependency on new migrations in A without having to do it by hand each time I modify a model in A?
I tried to add empty modifications of models in A at the migration B.0001, but I haven't got it to work and it looks very hacky to me.
I found a solution that doesn't exactly solve what I asked, but can be a workaround for some people, so I will post it here.
There is an undocumented field of the django.Migration class, called run_before, that works just like an inverse dependencies.
A.0002 will depend on B.0001 if I simply define run_before = [ A.0002 ] at B.0001.

Renaming Django model without breaking existing migrations

I want to rename a model in Django 3.2, keep my existing migrations and be able to both migrate a db with the old table name and create a db from scratch.
I've started by renaming the model class and all references to it in the code. As "./manage.py makemigrations" did not automatically create a migration, I manually created a migration that renames the model:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('market_integrations', '0003_migration'),
]
operations = [
migrations.RenameModel('OldModelName', 'NewModelname')
]
My initial idea is that I should not update existing migrations, as I never do when creating other migrations. However, the references to the old model in the old migrations cause a LookupError when I run "./manage.py migrate". I've tried using both the model name string and apps.get_model(). Migration code samples that break:
operations = [
migrations.CreateModel(
name="OldModelName",
...
)
]
operations = [
migrations.CreateModel(
name=apps.get_model("myapp", "OldModelName"),
...
)
]
As keeping the old model name in old migrations didn't work, I replaced the old model name in old migrations with the new name. After doing that, "./manage.py migrate" ran successfully, including the model renaming migration. However, when I try to create a new database, the model renaming migration fails because that new database never had a table with the old name.
What should I do to be able to preserve my migrations and have them working in both existing and new databases?
I have already checked Django migration strategy for renaming a model and relationship fields , but I didn't find the answer to my question there.
Please attach the full model (you can delete irrelevant fields) and the migration you created in your initial question (I cannot comment on your initial post yet, so I have to mention it in an answer).
This is important because I think there are foreign keys pointing to the model you want to rename.
Or are you trying to rename Foo to Bar and create a new Foo?
Does the error also occur if you try to start the server after your initial idea?
Your initial idea of keeping existing migrations untouched seems good, because you will be able to run it on all your environments (local, dev, prod, etc), and approach 2 would require manual renaming of database tables and fields.
I might be able to extend my answer after you provide models and migrations.
migrations.RenameModel(
old_name="ModelA",
new_name="ModelB",
),
migrations.AlterField(
model_name="myrelatedmodel",
name="somefieldname",
field=models.ForeignKey(
# change the properties accordingly (best copy it over). The database may only change to="table"
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="myapp.modelb",
),
),
If this did not work, where does the error come from? You can try to isolate your migrations and models from the rest of the code (like in a new empty project attached to the same database). Maybe this will help you find out if the error is from a leftover reference to OldModelName or some weird error. Just as an idea so you don't spend time looking at the wrong place
Try to do one change and one migration at a time. You can later squash them. If django does not auto-detect the renaming of your model, it means that all fields related to this model will fail too.
Revert everything to the working state before renaming
Make sure all migrations are applied
Rename the model and create a migration to only rename the model. Are you able to apply it?
if not, try to manually modify all related fields (one2one, foreignkey, etc)
If that still fails, create a copy of your model in models.py, rename the copied model and try to use migrations.RunPython() in a new migration file to copy all the data from the old table to the new one.
If you only have one populated database, you might get away with replacing all ModelA occurrences with ModelB and change the structure of your database.
Best of luck!

How can I remove a Django migration file?

I made a migration, and realised that I made an error (by adding a default value), I then made a new migration which allows for null.
I don't want my colleagues to run the first migration which adds a default value to thousands of records. How can I delete that migration without breaking the current migrations (usually if you just delete a migration you get a heap of errors which are a pain to fix).
I'd assume you could use a command? I'd assume it'd be something like this ~>
e.g django manage.py deletemigration <migration_id>
Squash
You can do a ./manage.py squashmigrations since one of your migrations are effectively cancelling out another the end result will be the field being nullable. Your colleagues will not have to go through the step of adding a default value.
Squashing is the act of reducing an existing set of many migrations
down to one (or sometimes a few) migrations which still represent the
same changes.
Edit the migration file
You can edit the migration file by hand to remove the changes to the column. A migration can actually have an empty migration
class Migration(migrations.Migration):
dependencies = [
(some stuff here),
]
operations = []

South migration of parent class with mulitple child classes in same app

I'm having an issue migrating a Django class which is a parent of two child classes. All classes are in the same app. Whenever I try to migrate the parent class, South complains that a table already exists. Here are my classes, simplified:
class ParentClass(models.Model):
my_field_in_both = models.DateField(null=True, blank=True)
class Meta():
abstract = True
The two child classes:
class ChildOne(ParentClass, AnotherMixin):
child_field = models.DateField(null=True, blank=True)
class ChildTwo(ParentClass, YetAnotherMixin):
another_child_field = models.DateField(null=True, blank=True)
Now, I can migrate either AnotherMixin or YetAnotherMixin classes no problem. But adding a field on the ParentClass, and then running:
python manage.py schemeamigration <appname> --auto
produces the migration file, but then running:
python manage.py migrate <appname>
gives:
django.db.utils.DatabaseError: table "_south_new_<appname>_ChildTwo" already exists
What am I doing wrong?
It says it already exists because the table already exists. It sounds like you messed up your migrations at some point in the past, but don't worry! Everyone does that at some point.
There are two routes you could take. If one fails, you can still do the other, so Im just going to list them in the order that I'd try them. The easier (and potentially riskier depending on the size of your project and how far you are along) of the two is to try to fake the migration.:
./manage.py migrate ChildTwo --fake
It's going to pretend it did everything correctly, but if there are conflicts with the actual structure of the database and the faked structure of the database, you're going to have to fix things manually. But it sounds like you're not very far in your project, so Im going to go out on a limb here and say that wont be that big of a deal. These are the things you're going to manually delete:
the migrations/ folder in your app.
the <app_name>_childtwo table in your database
any rows in the south_migrationshistory table corresponding to the ChildTwo model. If you're using mysql:
DELETE FROM <database name>.south_migrationhistory WHERE app_name ='<app_name>';
But it may be easier to just delete all migrations corresponding to the entire app then start over with:
./manage.py schemamigration app_name --initial
Like any reasonable person, I despise writing SQL, so I suggest using the phpmyadmin interface if you have it.
I'll stop editing soon, but one last thing, if your app isn't too far along, you could also do ./manage.py reset <app_name> and just start over. You'll still have to get rid of the south migrations folder and db entries

Django 1.3 and South migrations

I have an existing project which extensively uses South migrations to load data into its tables.
Since upgrading to Django 1.3 our unit tests no longer run because they cannot find the data they rely on.
Is this behaviour is due to one of the backwards incompatible changes in 1.3
Is there an easy way for me to convert all these migrations into fixtures?
Yes, this behavior is due to this change.
There seems to be a workaround in South trunk (see https://bitbucket.org/andrewgodwin/south/changeset/21a635231327 ) so you can try South development version (it is quite stable in my experience).
You may try to change the DB name in settings (in order to get clean environment), run ./manage.py syncdb and ./manage.py migrate and then do ./manage.py dumpdata
I hit this issue today. Eventually I ended up refactoring my migrations so that they use helper functions to actually insert the data, and then calling the same functions from the setUp() of my tests.
Some hints;
Make your helper functions take the model class as an argument, so you can call them with orm['yourapp.YourModel'] from the migration and with models.YourModel from the test. That also shows the main limitation: South works for models whose schema has changed since then, the test code can't do that. I was lucky in that this particular model hasn't changed.
If you want to keep the helper methods inside the migrations, you'll find that you can't directly import yourapp.migrations.0001_some_migration because identifiers can't start with numbers. Use something like migration_0001 = importlib.import_module('yourapp.migrations.0001_some_migration') instead of an import statement.