{"id":654,"date":"2018-02-13T12:33:05","date_gmt":"2018-02-13T12:33:05","guid":{"rendered":"http:\/\/www.piglets.org\/blog\/?p=654"},"modified":"2018-02-13T12:33:05","modified_gmt":"2018-02-13T12:33:05","slug":"migrating-django-migrations-to-django-2-x","status":"publish","type":"post","link":"https:\/\/www.piglets.org\/blog\/2018\/02\/13\/migrating-django-migrations-to-django-2-x\/","title":{"rendered":"Migrating Django Migrations to Django 2.x"},"content":{"rendered":"<p><a href=\"https:\/\/www.djangoproject.com\/\">Django<\/a> is a <a href=\"https:\/\/www.python.org\/\">Python<\/a> framework for making web applications, and its impressive in its completeness, flexibility and power for speedy prototyping.<\/p>\n<p>It's also an impressive project for forward planning, it has a kind of built in \"lint\" functionality that warns about deprecated code that will be disallowed in future versions.<\/p>\n<p>As a result when <a href=\"https:\/\/docs.djangoproject.com\/en\/2.0\/releases\/2.0\/\">Django 2.0 was released<\/a> I didn't have to make many changes to my app code base to get it to work successfully. However, today when I tried to update my oldest <a href=\"https:\/\/github.com\/profcturner\/WAM\">Django App<\/a> (started in Django 1.8x) I hit an unexpected snag. The old migrations were sometimes invalid. Curiously I don't think this problem emerged the last time I tried.<\/p>\n<p>Django uses migrations to move the database schema from one version to the next. Most of the time it's a wonderful system. In the rare case it goes wrong it can be ... <a href=\"http:\/\/www.piglets.org\/blog\/2015\/12\/07\/manually-completing-a-botched-django-migration\/\">tricky<\/a>. Today's problem is quite specific, and easier to fix.<\/p>\n<p>Django 2.0 enforces that <strong>ForeignKey<\/strong> fields explicitly specify a behaviour to follow on deletion of the object pointed to by the key. In general whether we Cascade the deletion, or set the field to Null, getting the behaviour write can be important, particular on fields where a Null value has a legitimate meaning.<\/p>\n<p>But a bit of a sting in the tail is that an older Django project may have migrations created automatically by Django which don't obey this. I discovered this today and found I couldn't proceed with my project unless I went back and modified the old migrations to be 2.0 compliant.<\/p>\n<p>So if this happens to you, here are some suggestions on fixing the problem.<\/p>\n<p>You will know if you have a problem if when you try to run your test server, or indeed replace <strong>runserver<\/strong> by <strong>check<\/strong><\/p>\n<pre class=\"lang:default decode:true\">python3 manage.py runserver<\/pre>\n<p>you get an error and output like this<\/p>\n<pre class=\"lang:default decode:true \" title=\"Typical example of an error\">  File \"\/Users\/colin\/Development\/WAM\/WAM\/loads\/migrations\/0024_auto_20160627_1049.py\", line 7, in &lt;module&gt;\r\n    class Migration(migrations.Migration):\r\n  File \"\/Users\/colin\/Development\/WAM\/WAM\/loads\/migrations\/0024_auto_20160627_1049.py\", line 100, in Migration\r\n    field=models.ForeignKey(null=True, to='loads.ActivitySet', blank=True),\r\nTypeError: __init__() missing 1 required positional argument: 'on_delete'\r\n<\/pre>\n<p>I would suggest you try <strong>runserver<\/strong> whatever you did before as it will continue to try each time you save a file.<\/p>\n<p>Open your code with your favourite editor, and open your models.py file (you may have several depending on your project), and the migration file that's broken as above.<\/p>\n<p>Looking in your migration file you'll find the offending line. In this case it's the last (non trivial) line below.<\/p>\n<pre class=\"lang:python decode:true\" title=\"Snippet from broken migration.\">      migrations.AddField(\r\n            model_name='activity',\r\n            name='activity_set',\r\n            field=models.ForeignKey(null=True, to='loads.ActivitySet', blank=True),\r\n),<\/pre>\n<p>To ensure that your migrations will be applied consistently with your final model (well, as long as nobody tries to migrate to an intermediate state) look carefully in the correct model (<strong>Activity<\/strong>) in this case, and see what decision you make for deletion there. In my case I want deletion of the <strong>ActivitySet<\/strong> to kill all linked <strong>Activitiy<\/strong>(s). So replicate the \"on_delete\" choice from there.<\/p>\n<pre class=\"lang:default decode:true\" title=\"Snippet from fixed migration\">      migrations.AddField(\r\n            model_name='activity',\r\n            name='activity_set',\r\n            field=models.ForeignKey(null=True, to='loads.ActivitySet', on_delete=models.CASCADE, blank=True),\r\n),<\/pre>\n<p>Each time you save your new migration file the <strong>runserver<\/strong> terminal window will re-run the check, hopefully moving on to the next migration that needs to be fixed. Work your way through methodically until your code checks clean. Check into source control, and you're done.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Django is a Python framework for making web applications, and its impressive in its completeness, flexibility and power for speedy prototyping. It's also an impressive project for forward planning, it has a kind of built in \"lint\" functionality that warns about deprecated code that will be disallowed in future versions. As a result when Django [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"vkexunit_cta_each_option":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[7,6,14],"tags":[131,141,58],"class_list":["post-654","post","type-post","status-publish","format-standard","hentry","category-11-free-software","category-7-programming","category-17-python","tag-django","tag-migration","tag-python"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"jetpack_shortlink":"https:\/\/wp.me\/p52I4w-ay","_links":{"self":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/comments?post=654"}],"version-history":[{"count":1,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/654\/revisions"}],"predecessor-version":[{"id":655,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/posts\/654\/revisions\/655"}],"wp:attachment":[{"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/media?parent=654"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/categories?post=654"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.piglets.org\/blog\/wp-json\/wp\/v2\/tags?post=654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}