Migrate models with from one django app to several other apps - python

I have a django app which consists of 17 models. Now I have realized that these models should be in 3 different apps(not in the original app). So now I would like to migrate these models out of the original app to these 3 different apps. How do I do that?
There exists foreign key, generic foreign key and ManyToMany relationships among the models. I also have data in the database(MySql), so I would like the data to be preserved during migration.
I have installed south for migrations, but don't know how to use it for solving this issue. I have gone through this similar question but could not find an answer that would solve my problem. Would be thankful for any help !

In my opinion, you have two ways of completing this task as stated below:
Move the models and add Meta.db_table to refer the existing sql table as needed as #kroolik suggested
Perform a three steps migration
The former is easier while the later could be better as tables would be named as you expect.
First of all, you mention you already has south installed. The first step would be to create the initial migration for the existing app. Take a look to the south tutorial. Then you must apply that migration, but as you already has the tables in db it would fail unless you include --fake flag.
After that you need to create the three apps you mention, and their models. Also create and apply (this time without fake flag) the initial migration for them.
Next step is write a datamigration. You must write it manually, although you can create the skeleton with datamigration. You must write "by hand" the migration.
Now you are almost done, the only remaining thing is remove the original tables. You can just remove those models, and create an "auto" schemamigration.
Don't forget to apply the migrations with migrate command. Also as #Bibhas mention a copy of database and/or a dump of it is a pretty good idea.

Related

Freezing database for testing new features in Django

In my Django app, I want to add a couple of fields to my existing models and possibly create a new class. I just want to test the new feature and approve if it works.
I can revert the code using git easily. But if I make a makemigrations+migrate then my MySQL database will change and reversing the changes looks like manual deletion of tables and reverting to an old state using a command like django-admin migrate [app_label] [migration_name] (In some cases it looks really cumbersome, example).
I'm wondering if there is any safe practice to try manipulating the database and revert it back to it's initial state safely.
Probable solution #1:
You can utilize the test database that gets created when using django.test.TestCase:
Tests that require a database (namely, model tests) will not use your
“real” (production) database. Separate, blank databases are created
for the tests.
Create some unit tests for your project and make your migrations (without migrating to your production DB, just keep the migrations). Then:
If the database does not exist, it will first be created. Any
migrations will also be applied in order to keep it up to date.
Usually, the database gets destroyed at the end of your tests, but you can keep it between runs:
You can prevent the test databases from being destroyed by using them
test --keepdb option. This will preserve the test database between
runs.
With this trick you can test every migration you make in a fake DB and when you do finalize your model and you have all the migrations history complete, you can migrate on your production DB.
Probable solution #2:
You can make a copy of your database as #albar suggests and have it as a back up while you are working on your new migrations.
Break stuff as much as you want and when you are set and done, replace the "battered" DB with your back up and apply your migration history to it.

django migrations - workflow with multiple dev branches

I'm curious how other django developers manage multiple code branches (in git for instance) with migrations.
My problem is as follows:
- we have multiple feature branches in git, some of them with django migrations (some of them altering fields, or removing them altogether)
- when I switch branches (with git checkout some_other_branch) the database does not reflect always the new code, so I run into "random" errors, where a db table column does not exist anymore, etc...
Right now, I simply drop the db and recreate it, but it means I have to recreate a bunch of dummy data to restart work. I can use fixtures, but it requires keeping track of what data goes where, it's a bit of a hassle.
Is there a good/clean way of dealing with this use-case? I'm thinking a post-checkout git hook script could run the necessary migrations, but I don't even know if migration rollbacks are at all possible.
Migrations rollback are possible and usually handled automatically by django.
Considering the following model:
class MyModel(models.Model):
pass
If you run python manage.py makemigrations myapp, it will generate the initial migration script.
You can then run python manage.py migrate myapp 0001 to apply this initial migration.
If after that you add a field to your model:
class MyModel(models.Model):
my_field = models.CharField()
Then regenerate a new migration, and apply it, you can still go back to the initial state. Just run
python manage.py migrate myapp 0001 and the ORM will go backward, removing the new field.
It's more tricky when you deal with data migrations, because you have to write the forward and backward code.
Considering an empty migration created via python manage.py makemigrations myapp --empty,
you'll end up with something like:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def forward(apps, schema_editor):
# load some data
MyModel = apps.get_model('myapp', 'MyModel')
while condition:
instance = MyModel()
instance.save()
def backward(apps, schema_editor):
# delete previously loaded data
MyModel = apps.get_model('myapp', 'MyModel')
while condition:
instance = MyModel.objects.get(myargs)
instance.delete()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0003_auto_20150918_1153'),
]
operations = [
migrations.RunPython(forward, backward),
]
For pure data-loading migrations, you usually don't need the backward migration.
But when you alter the schema and update existing rows
(like converting all values in a column to slug), you'll generally have to write the backward step.
In our team, we try to avoid working on the same models at the same time to avoid collision.
If it is not possible, and two migration with the same number (e.g 0002) are created,
you can still rename one of them to change the order in which they will be applied (also remember to update
the dependencies attribute on the migration class to your new order).
If you end up working on the same model fields at the same time in different features,
you'll still be in trouble, but it may mean these features are related and should be handled
together in a single branch.
For the git-hooks part, it's probably possible to write something, Assuming your are on branch mybranch
and want to check out another feature branch myfeature:
Just before switching, you dump the list of currently applied migrations into
a temporary file mybranch_database_state.txt
Then, you apply myfeature branch migrations, if any
Then, when checking back mybranch, you reapply your previous database state
by looking to the dump file.
However, it seems a bit hackish to me, and it would probably be really difficult to handle properly all scenarios:
rebasing, merging, cherry-picking, etc.
Handling the migrations conflicts when they occurs seems easier to me.
I don't have a good solution to this, but I feel the pain.
A post-checkout hook will be too late. If you are on branch A and you check out branch B, and B has fewer migrations than A, the rollback information is only in A and needs to be run before checkout.
I hit this problem when jumping between several commits trying to locate the origin of a bug. Our database (even in development trim) is huge, so dropping and recreating isn't practical.
I'm imagining a wrapper for git-checkout that:
Notes the newest migration for each of your INSTALLED_APPS
Looks in the requested branch and notes the newest migrations there
For each app where the migrations in #1 are farther ahead than in #2, migrate back to the highest migration in #2
Check out the new branch
For each app where migrations in #2 were ahead of #1, migrate forward
A simple matter of programming!
For simple changes I rely on migration rollback, as discussed by Agate.
However, if I know a feature branch is going to involve highly invasive database changes, or if it will involve a lot of data migration, I like to create a clone of the local (or remote dev) database as soon as I start the new branch. This may not always be convenient, but especially for local development using sqlite it is just a matter op copying a file (which is not under source control).
The first commit on the new branch then updates my Django settings (local/dev) to use the cloned database. This way, when I switch branches, the correct database is selected automatically. No need to worry about rolling back schema changes, missing data, etc. No complicated stuff.
After the feature branch has been fully merged, the cloned database can be removed.
So far I have found two Github projects (django-south-compass and django_nomad) that try to solve the issue of migrating between dev branches and there is a couple of answers on Stack Overflow.
Citing an article on Medium, most of the solutions boil down to one of the following concepts:
Dropping all the tables and reapply migrations in the target branch from scratch. When the tables are created from scratch, all the data will be lost and needs to be recreated as well. This can be handled with fixtures and data migrations but managing them, in turn, will become a nightmare, not to mention that it will take some time (...)
Have a separate database for each branch and change the settings file with the target branch’s settings every time the branch is switched using tools like sed. This can be done with a post_checkout hook. Maintaining one large database for each branch would be very storage-intensive. Also, checking out individual commit IDs might potentially produce the same errors.
Finding the differences in migrations between the source and target branch, and apply the differences. We can do so with post_checkout script but there is a small issue. This post explains the issue in detail. To summarize the issue, post_checkout is run after all the files in the target branch are checked out, which includes migration files. If the target branch doesn’t contain all the migrations in the source branch when we run python manage.py migrate app1 Django won’t be able to find the missing migrations which are needed to apply reverse migrations. We have to temporarily checkout migration files in the source branch, run python manage.py migrate and checkout migration files in the target branch. django-south-compass does something very similar but is available only for up to python 2.6.
Using a management command (which uses python git module), find all the migration operations differences between the source branch and the merge-base of the source branch and target branch and notify the user of these changes. If these changes don’t interfere with the reason for branch change, the user can go ahead and change the branch. Else, using another management command, un-apply all migration till merge base, switch branch, and apply the migrations in the target branch. There will be a small data loss and if the two branches haven’t diverged a lot, is manageable. django_nomad does some of this work.
Keep a track of applied and unapplied migrations in files and use this data to populate the tables when switching branches.

Remake Django Model Every time

I started making my project without realizing that every time I populate my DB table it adds to the existing data. I thought it would delete the old data and add the new data!
Is there any way to do what I've described above easily? If not how would you suggest I do something like this?
You should probably be using migrations:
https://docs.djangoproject.com/en/1.8/topics/migrations/
You should read the Models tutorial, toward the end it talks about migrations:
https://docs.djangoproject.com/en/1.8/intro/tutorial01/
If you are using Django 1.6 or previous, you should use south to manage your migrations:
http://south.readthedocs.org/en/latest/
Migrations helps managing versions of the Model, every time you change your model, you create a migration and apply it. Creating the migration can be automatic, south and django >= 1.7 both can find changes in your models and create the migration files. The migration files are python files capable of making the changes to the DB. Then you apply the migration and the change is done in the DB. To create the migrations, and to apply them, you must manually run a command.
The reason for having such files is version management, you can get back to a previous migration and have your DB as before. Read the documentation to learn the simple commands to do it, you surely will get it in a couple hours.

Creating migrations for auth app in django

I need to add a couple fields to Group model in django contrib.auth app using:
field_name = models.CharField(...)
field_name.contribute_to_class(Group, 'field_name')
My issue is while creating the migrations with South, since they are created in the "migrations" directory inside the auth app and, since the system is already in production, I'm not allowed to alter the current django installation in the server in order to migrate auth.
Does anyone know how to create and load such migrations?
Thanks in advance for your help.
Django doesn't make it particularly easy to modify the standard models. I wouldn't recommend that you sublass Group, because it's quite annoying to get built-in functionality to reference the new model instead.
The usual thing to do here is to create a GroupProfile model that has a Group as a unique foreign key. It might not be elegant, but it won't have the huge maintenance overhead that comes with forking Django's source code.
Also: if you can't modify the Django code on the server, you aren't going to be able to do this with raw SQL hackery or a clever migration. South isn't going to be the problem -- the problem is that the Django ORM is going to notice that there are fields present in the SQL table that aren't specified in the code, which will cause it to throw an exception.
Since you use a hack to patch up the model, I think you should write a migration manually. Try copying another migration and changing add_column and models first, if it fails - there is always an option named "raw sql" :)

Django: Change models without clearing all data?

I have some models I'm working with in a new Django installation. Is it possible to change the fields without losing app data?
I tried changing the field and running python manage.py syncdb. There was no output from this command.
Renavigating to admin pages for editing the changed models caused TemplateSyntaxErrors as Django sought to display fields that didn't exist in the db.
I am using SQLite.
I am able to delete the db file, then re-run python manage.py syncdb, but that is kind of a pain. Is there a better way to do it?
Django does not ever alter an existing database column. Syncdb will create tables, but it does not do 'migrations' as found in Rails, for instance. If you need something like that, check out Django South.
See the docs for syndb:
Syncdb will not alter existing tables
syncdb will only create tables for models which have not yet been installed. It will never issue ALTER TABLE statements to match changes made to a model class after installation. Changes to model classes and database schemas often involve some form of ambiguity and, in those cases, Django would have to guess at the correct changes to make. There is a risk that critical data would be lost in the process.
If you have made changes to a model and wish to alter the database tables to match, use the sql command to display the new SQL structure and compare that to your existing table schema to work out the changes.
You have to change the column names in your DB manually through whatever administration tools sqlite provides. I've done this with MySQL, for instance, and since MySQL lets you change column names without affecting your data, it's no problem.
Of course there is.
Check out South
You'll have to manually update the database schema/layout, if you're only talking about adding/removing columns.
If you're attempting to rename a column, you'll have to find another way.
You can use the python manage.py sql [app name] (http://docs.djangoproject.com/en/dev/ref/django-admin/#sql-appname-appname) command to see what the new SQL should look like, to see what columns, of what type/specification Django would have you add, and then manually run corresponding ALTER TABLE commands.
There are some apps/projects that enable easier model/DB management, but Django doesn't support this out of the box, on purpose/by design.

Categories