Where are the historical models? - django

The doc says:
When you run migrations, Django is working from historical versions of your models stored in the migration files.
But I can't see them there. I have data migrations with RunPython operations, but no historical models there as well. Could it be that Django generates them on the fly? How does it do it?
And while we're at it, let me confirm if I understand it correctly. Historical models are models as they were when a migration was written? Except for some limitations, like no custom methods.

Whenever you create a model and run makemigrations. Django creates a migration file for that model which signifies the creation of such Model.
operations = [
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
],
)
]
When you edit that model like adding a field and again run makemigrations, a new migration file is created for that change. This is the way Django stores history of that model.
operations = [
migrations.AddField(
model_name='book',
name='author',
field=models.CharField(blank=True, max_length=50, null=True),
),
]
The migration files are the historical models.
Now when you add some custom Python code with RunPython in migration it will be a part of the historical context for future migrations.
Migration files are set of operations that are needed to be done on database for a model. They do not have the customization of the model like save() method because this model is created from the history i.e. migration files.

Related

django-oscar2.1 giving errors on is_public field. Its a model loading issue

I am migrating to django-oscar2.1
I have forked catalogue app, it has some existing migrations. Newly added field is_public is in migration #10. I have a custom migration #5 which is using create_from_breadcrumbs method. So when I tried to run migrate on fresh db migration#5 trigger error is_public field not exists in db.
create_from_breadcrumbs
Oscar loads a model with latest state which contains is_public in models but not in db.
In migration #5 I am loading model like this
Category = apps.get_model("catalogue", "Category")
You cannot use create_from_breadcrumbs in a migration like that. The reason for this is explained in Django's documentation on migrations - that the version of the model used in external code may be different from the one that this migration expects - which is why it fails.
If you want to create model objects as part of your migration you need to use the historic version of the model in the migration. This means defining your own version of create_from_breadcrumbs, and using the historic version of the model as suggested by the Django documentation:
def create_from_breadcrumbs_for_migration(apps, schema_editor):
Category = apps.get_model('catalogue', 'Category')
# Perform logic to create categories here.
class Migration(migrations.Migration):
dependencies = [
('catalogue', '0004_xxxx'),
]
operations = [
migrations.RunPython(create_from_breadcrumbs_for_migration),
]
That said, I would argue that migrations are not meant for this sort of data population in the first place. You would in my view be better off writing a separate management command for this, which you run after the migrations have been applied.

Issues in Django migrations

I ran into a difficult problem related to Django migrations and searching for answer
I created few database models and inserted data on those models by using migrations file
Example Migrations 0001_users.py
Example Model :
migrations.CreateModel(
name='User',
fields=[
('user_id', models.CharField(max_length=256)),
('name', models.CharField(max_length=256)),
],
)
Example Migrations 0002_users_data.py
Example Data:
user = User(user_id="DEV", name="dev")
user.save()
user = User(user_id="STAGE", name="stage")
user.save()
Now i add an ForeignKey in above table say address with some default value in Migrations file 0003_updated_column.py and its working fine locally,as i have already created a table, inserted data and now modifying old data with new column
But, If any new user want to pull as a fresh repo, he is getting issues in running migrations as my first,second migrations file will have hard-coded users table data with out newly added ForeignKey on it but models file will have newly added ForeignKey as well
Please help me on this

Django makemigrations make change for 'auth.user' every time

I upgrade my system's django version from 1.6.10 to 1.8.16 for test.
On before version, i use South for migration.
So, I followed 'https://docs.djangoproject.com/en/1.7/topics/migrations/#upgrading-from-south' this documentation.
My problem is every makemigrations are check same field, then make migration file.
That field is 'auth.User' foreign key field. like user = models.ForeignKey('auth.User').
here are my screenshot for that problem.
This is Sample code for that foreign key field.
cancelled_by = models.ForeignKey(
'auth.User',
verbose_name=_("Cancelled by"),
related_name='project_cancel',
blank=True,
null=True
)
How can i fix it?
edited:
This is my migration file created by makemigrations after all migration.
class Migration(migrations.Migration):
dependencies = [
('meeting', '0003_meeting_proposal'),
]
operations = [
migrations.AlterField(
model_name='meeting',
name='manager',
field=models.ForeignKey(verbose_name='Manager', blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
]
I would first delete the recently made migration files and try makemigration again.
deleting migration files is a common solution since Django is smart enough to recreate them easily regardless of what has been changed in your folder.
you can fake these migrations too, but i prefer to keep my migration folder clean and tidy.

Django 1.8 migration: any way to get data from database table that no longer has a model?

I'm trying to rename a model and I would like to write the migration in the way that it doesn't depend on the old name still present while it being applied. Can I somehow get data from a database table that no longer has a model in my migration code?
Details:
I have a Region model that I want to move into a more generic GeoObject model and remove from the models.py. If I write my migration code that creates GeoObjects from existing Regions with from models import Region I'll have to keep Region model until my main database will migrate. But I'd like to write a migration so that it doesn't depend on Region model being present, just check that the database table exists and use it. Is it possible to do it using Django instruments, without depending on a specific database type if possible?
Yes, you can.
But first of all, you really shouldn't import any model inside migration.
Take look at RunPython operation, that will allow you to run any python code inside your migration. RunPython will pass to your function 2 parameters: apps and schema_editor. First parameter contains structure of your models at stage of applying that migration, so if actual removing of model is later on that migration, you can still access that model using apps passed into function.
Let's say your model looked like this:
class SomeModel(models.Model):
some_field = models.CharField(max_length=32)
Now you're deleting that model, automatically created migration will contain:
class Migration(migrations.Migration):
dependencies = [
('yourapp', '0001_initial'), # or any other dependencies
]
operations = [
migrations.DeleteModel(
name='Main',
),
]
You can modify that migration by injecting RunPython just above DeleteModel operation:
operations = [
migrations.RunPython(
move_data_to_other_model,
move_data_back, # for backwards migration - if you won't ever want to undo this migration, just don't pass that function at all
),
migrations.DeleteModel(
name='SomeModel',
),
]
and creating 2 functions before Migration class:
def move_data_to_other_model(apps, schema_editor):
SomeModel = apps.get_model('yourapp', 'SomeModel')
for something in SomeModel.objects.all():
# do your data migration here
o = OtherModel.objects.get(condition=True)
o.other_field = something.some_field
def move_data_back(apps, schema_editor):
SomeModel = apps.get_model('yourapp', 'SomeModel')
for something in OtherModel.objects.all():
# move back your data here
SomeModel(
some_field=something.other_field,
).save()
It doesn't matter that your model is no longer defined in models.py, django can rebuild that model based on migration history. But remember: save method from your models (and other customized methods) won't be called in migrations. Also any pre_save or post_save signals won't be triggered.

Django AutoField wrong migration for "id" field

I have quite simple Django model:
class MyModel(models.Model):
user = models.ForeignKey(User)
somestring = models.CharField(
max_length=250
)
... some other string fields...
There is no declared "id" field in model, so it has automatic primary key assigned by Django.
Initial migration looked like this:
migrations.CreateModel(
name='MyModel',
fields=[
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('somestring', models.CharField(max_length=250))
... some other string fields...
],
options={},
bases=(models.Model,),
),
It was successfully applied and database table contains fields like:
- id <== autogenerated
- user
- somestring
etc...
Occasionally I've started to get messages like
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.
After running manage.py makemigrations it generated very strange migration:
$ ./manage.py makemigrations
You are trying to add a non-nullable field 'id' to mymodel 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: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now()
>>>
Please enter some code, or 'exit' (with no quotes) to exit.
>>> ''
Migrations for 'myapp':
0036_auto_20150623_1535.py:
- Add field id to mymodel
It loosk like this:
class Migration(migrations.Migration):
dependencies = [
('foo', 'bar'),
]
operations = [
migrations.AddField(
model_name='mymodel',
name='id', <== !!!
field=models.AutoField(auto_created=True, primary_key=True, default='', serialize=False, verbose_name='ID'),
preserve_default=False,
),
]
But this migration doesn't makes sense and sure it fails, because there is already "id" field in appropriate DB table.
The quick and dirty solution is to --fake this migration. Which should work locally on dev machine, but can result into migration errors on other environments (test/staging/prod).
Looks like the model's old/new fields state was calculated incorrectly, so that "id" wasn't included in old model but it is included in new one, so django descided that id field should be added.
Not sure about the cause, but my main question is - What is the proper approach to reset the model or migration state and return it to the previous one, when there is no any need in such migration?
Alright, found a viable workaround for this.
Added "id" field declaration into original migration, so it is no longer reported as missing.
migrations.CreateModel(
name='MyModel',
fields=[
!!===> ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('somestring', models.CharField(max_length=250))
... some other string fields...
],
options={},
bases=(models.Model,),
),
This change will not be applied on environments where we already have this migration and new environments should get the proper db table structure from the start.
TIL: Django DOES NOT use SQL to fetch the current model state from DB, - but create mock of the model and applies the migrations to that mock until it gets the latest version of model according all available migration operations. And then that reconstructed model is compared to real one from models.py.
Adjusting the original migration solved the issue. Thanks to everyone who participated in discussions.