django/south : redundant add_column? - django

I made a change, adding a unique constraint to a model, within the abc application and did a
./manage.py schemamigration abc --auto
That created a migration file but as well as the expected change the new migration file also contained a number of add_column statements which are adding columns which were previously added in an earlier migration (and which have been the subject of a migrate)
I'm really puzzled as to why this has happened and what to do about it.
Will the add_column statements just be ignored if I do another migrate ?

OK thanks to the #django-south irc channel I've figured this out.
This type of problem can arise when activity has taken place in different source control branches and, as a result of a merge, the dictionary of frozen models, which appears at the bottom of a south migration file, is missing some stuff which has already taken place. The result of this is that the next schemamigration tries to produce the "missing" changes.
The fix is to manually edit the migration file which was created by the schemamigration before doing migrate. This will get things back into synch.
There's some information about issues in the later part of this section : http://south.readthedocs.org/en/latest/tutorial/part5.html#team-workflow .
Thanks to carljm and maney on #django-south for helping me with this.

Related

Migrating to Django 2.2 from 1.11 -- old migrations without on_delete in ForeignKey break everything [duplicate]

This question already has an answer here:
Migrating problems when porting Django project to Python 3 and Django 2
(1 answer)
Closed 3 years ago.
I'm working on upgrading my legacy application from Django 1.11.13 to 2.2.8. I've dutifully addressed every compatibility issue, but I've hit one I can't figure out how to resolve. When I try to start the webserver in my local environment, I get this error (only showing the end of the full error trace that appears):
File "/Users/me/my_app/my_model/migrations/0001_initial.py", line 37, in Migration
('entry', models.ForeignKey(to='my_model.Entry')),
TypeError: __init__() missing 1 required positional argument: 'on_delete'
I understand why on_delete is now required -- I just spent a while updating my models everywhere to accommodate this change -- but I have no idea how to fix this particular issue without going through dozens of old migrations files to make them conform?!
I tried squashmigrations to at least collapse the number of places I have to clean up, but I got the same exact TypeError.
I tried to use the old version of Django for squashmigrations. I was successful in avoiding the TypeError, but ended up with a huge mess of circular import errors.
Since I don't actually need the migration history to roll back, I tried to follow these instructions (scenario 2) to clear the migration history while keeping the existing database, but I couldn't run makemigrations to catch up on the changes I made to make my models Django 2.2 compliant, and when I decided I'd skip ahead and deal with that later, showmigrations failed with the same TypeError. (Is there some other way to get a fresh set of initial migrations based on the current database? It can't be based off the models since the models have upgrade-related changes not yet reflected in the database.)
I moved the migrations to a non-standard location, which got the server to start, but that makes it impossible to actually do anything migration related ever again, and of course once I move back, everything breaks again...
I've considered just deleting my entire database and all migration history, building the tables from scratch with a fresh set of initial migrations, and then resetting the data from a backup, but there are a few huge tables which would make this take quite a while... and this rather seems like the nuclear approach. Am I stuck with editing a large number of very old migrations to be compliant with Django 2.2 for no actual reason since I'm never going to roll my project that far back? How can that be right?
As Iain Shelvington mentions in a comment under the question,
First delete all of your migration files and folder, then run makemigrations with the "on_delete" - this should create some "initial" migration files. Then you'll have to log in to your DB and delete all entries for your apps and then you need to run manage.py migrate --fake - this will enter into the DB entries for the newly created migrations but will not apply them

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 = []

How to unittest a django database migration?

We've changed our database, using django migrations (django v1.7+).
The data that exists in the database is no longer valid.
Basically I want to test a migration by, inside a unittest, constructing the pre-migration database, adding some data, applying the migration, then confirming everything went smoothly.
How does one:
hold back the new migration when loading the unittest
I found some stuff about overriding settings.MIGRATION_MODULES but couldn't work out how to use it. When I inspect executor.loader.applied_migrations it still lists everything. The only way I could prevent the new migration was to actually remove the file; not a solution I can use.
create a record in the unittest database (using the old model)
If we can prevent the migration then this should be pretty straightforward. myModel.object.create(...)
apply the migration
I think I can probably work this out now that I've found the test_executor: set a plan pointing to the migration file and execute it? Um, right? Got any code for that :-D
confirm the old data in the database now matches the new model
Again, I expect this should be pretty easy: just fetch the instance created before the migration and confirm it has changed in all the right ways.
So the challenge is really just working out how to prevent the unittest from applying the latest migration script and then applying it when we're ready?
Perhaps I have the wrong approach? Should I create fixtures, and just confirm that they're all good at the end? Do fixtures get loaded before the migrations are applied, or after they're all done?
By using the MigrationExecutor and picking out specific migrations with .migrate I've been able to, maybe?, roll it back to a specific state, then roll forward one-by-one. But that is popping up doubts; currently chasing down sqlite fudging around due to the lack of an actual ALTER TABLE instruction. Jury still out.
I wasn't able to prevent the unittest from starting with the current database schema, but I did find it is quite easy to revert to earlier points in the migration history:
Where "0014_nulls_permitted" is a file in the migrations directory...
from django.db.migrations.executor import MigrationExecutor
executor.migrate([("workflow_engine", "0014_nulls_permitted")])
executor.loader.build_graph()
NB: running the executor.loader.build_graph between invocations of executor.migrate seems to be a very important part of completing the migration and making things behave as one might expect
The migrations which are currently applicable to the database can be checked with something like:
print [x[1] for x in sorted(executor.loader.applied_migrations)]
[u'0001_initial', u'0002_fix_foreignkeys', ... u'0014_nulls_permitted']
I created a model instance via the ORM then ensured the database was in the old state by running some SQL directly:
job = Job.objects.create(....)
from django.db import connection
cursor = connection.cursor()
cursor.execute('UPDATE workflow_engine_job SET next_job_state=NULL')
Great. Now I know I have a database in the old state, and can test the forwards migration. So where 0016_nulls_banished is a migration file:
executor.migrate([("workflow_engine", "0016_nulls_banished")])
executor.loader.build_graph()
Migration 0015 goes through the database converting all the NULL fields to a default value. Migration 0016 alters the schema. You can scatter some print statements around to confirm things are happening as you think they should be.
And now the test can confirm that the migration has worked. In this case by ensuring there are no nulls left in the database.
jobs = Job.objects.all()
self.assertTrue(all([j.next_job_state is not None for j in jobs]))
We have used the following code in settings_test.py to ignore the migration for the tests:
MIGRATION_MODULES = dict(
(app.split('.')[-1], '.'.join([app, 'nonexistent_django_migrations_module']))
for app in INSTALLED_APPS
)
The idea here being that none of the apps have a nonexistent_django_migrations_module folder, and thus django will simply find no migrations.

South not recognizing added model field?

My teammate and I have been using South for a little bit now with very minimal problems. We now just hit an issue where South isn't recognizing our added model field.
When I run
./manage.py schemamigration appname --auto
I keep getting "Nothing seems to have changed."
Miscellaneous yet random details:
South==0.8.2
I checked the db tables and the column is indeed not there.
Update #1: We created our own migration file to add it and it worked. We're just trying to figure out why using schemamigration didn't so we don't have to keep doing it manually..
Update #2: I have a feeling it might have to do with the fieldtype we're using..? I tried adding a charfield and south worked but when it comes to URL field there's no recognition whatsoever...
Answer: I found out what was wrong. Teammate was using a variable name that was also a method name. Can't answer my own question yet because I don't have enough rep but I will when I can.
I found out what was wrong: my teammate was using a variable name that was also a method name.

DatabaseError: value too long for type character varying(100)

I have a Django web site running a mini CMS we've built internally years ago, it's using postgresql. When saving a simple title and a paragraph of text I get the following error:
value too long for type character varying(100)
The weird thing is, not a single column is varying(100) they are all 200 or 250, even the default Django ones have been changed from the 100 to 200 due to a re-opened ticket mentioned here
Does anyone know of a solution to this problem?
I can bet money you have a models.SlugField without length set. The default length is 50 characters, most likely it's not enough for your use case.
Change it to models.SlugField(max_length=255) and migrate your database schema.
I also had this problem when using a filefield and was scratching my head for a while. Of course the default FileField instances are created with a 100 character limit.
https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
This is an error message from Postgres and not django.
You seem to have changed the length of the field in the models.py, but that doesn't change the database length which was created when you did a manage.py syncdb.
You have to alter the length of the field in the database, directly.
Django 2.1
I encountered this problem while switching from sqlite3 to postgresql.
Remove the migration files in each app's migrations folder except __init__.py
Then re-run migration
(venv)myapp$python manage.py makemigrations
(venv)myapp$python manage.py migrate
(venv)myapp$python manage.py runserver
I had a similar problem with django-autoslugfield
I was using a similar package and then switched over to django-autoslugfield
I was getting this error:
value too long for type character varying(50)
despite the fact that my models.py had:
slug = AutoSlugField(max_length=255, populate_from='name', unique=True)
and in my db the it the type was
character varying 255
once i remove max_length=255 from the field i.e.
slug = AutoSlugField(populate_from='name', unique=True)
then it worked fine
i went through this same error. and when i made changes in the modele, i kept having the same error.
Here is how i fixed it.
It might be necessary to skip few migrations for the program to only use the migration where changes have been made for the CharField max_lenght.
for that you have to run
python manage.py showmigrations
to see which migrations have not been made.
then you skip them until you get to the last with the command
python manage.py migrate <app> 000_migration_number --fake
I realize the question is already answered but for others that come here when looking for the error message:
In my case the problem was that my table name exceeded 50 characters. Apparently this is not allowed. Changing the table name solved the problem.
Read more here: https://code.djangoproject.com/ticket/18959
Michael Samoylov's answer pointed me in the right direction. I had the same error come up, except it was with a FileField.
Fields have a max_length, even if you have not explicitly set a max_length. Increase the value so that your data fits to avoid the error.
In my case, the data was too large to reasonably store in the database. I resorted to saving my file to disk, then saving the file path in the database.
I had this problem when I wanted to set max_length to a lower value on a FileField.
Interestingly, the error message states value too long but in my case the value was too short.
The solution was to set back the max_length to its old value. It's quite weird that the migration couldn't be done.
I also had to delete the wrongly generated migrations files and rerun python manage.py makemigrations and python manage.py migrate.
If it's not SlugField, FileField, or any other field mentioned here--scroll back to where the migration got stuck in the terminal. For me it was AddField
Good talk.
First, try setting max_length to something reasonable on all applicable field types in your model.
For example: MyText = models.CharField(max_length=2000)
If you don't set a max_length, your database might be applying a default max_length, shorter than the length of your input data, causing your value too long for type character error.
If that doesn't work and you started in SQLite and changed databases to PostgreSQL, the previous migrations from SQLite might be interfering with the new PostgreSQL migrations.
Go into the migrations folder for your project and delete the old migration files to get a fresh start. Then try makemigrations and migrate again :)
predefined fields in model.py creates the problem. Extend it to desired length, i think problem will be resolved.
For the FileField and the ImageField, from the Django docs:
FileField instances are created in your database as varchar columns with a default max length of 100 characters. As with other fields, you can change the maximum length using the max_length argument.
The value can be 100 or 25 or 17 whatever , the reason behind that is models , search where you have added that particular length(17,25) and you will get the bug ! ,
You are trying to impose a field length more than mentioned one.
This solved my issue , I hope it will help you too