Direct Drupal 5 to Drupal 7 Migration in 24hrs

With Drupal 7 around the corner, a lot of Drupal developers and users are surely looking at their lingering still-on-Drupal-5 sites and thinking, "surely I might be able to skip Drupal 6 and upgrade directly to Drupal 7?"

Most all experienced Drupal developers would say (to end-users), "not possible". This is because modules only provide upgrade paths from one major version to the next, and just about all Drupal modules received large rewrites between Drupal 5 and Drupal 6. Without help from module developers, it's not extraordinarily difficult to do a direct Drupal 5 to Drupal 7 migration, but you are going to be building out a lot more things manually.

Of all the projects I'm working on, only my personal blog was still on Drupal 5. As this is rather embarrassing, I figured at least it would be a good experience with direct migration from D5 to D7. Here are my experiences and the approach I used in this upgrade.

Don't bother with upgrading to D6 at all.With a jump this big, it seemed like a huge headache to download all of my Drupal 5 modules in their Drupal 6 versions. Especially because some modules have actually released a later D5 version than I was running, I'd actually need to upgrade my D5 modules first, then upgrade to D6, then upgrade to D7. No thanks.

Rebuild the basics.This includes building out Taxonomy vocabularies, Content types, and all the fields on those types. Fortunately with modules like Select options, ImageField, and FileField built into core, this goes much quicker than you might expect.

Scale back unneeded modules.I couldn't believe some of the modules I'd installed for tiny bits of functionality on my site. I had Date module installed for a single date which is never used for sorting or in Views. I had Link module for entering URLs but no title. I converted both of these to simple text fields using the now-provided core Text module. Other things that made sense for me included converting Upload module to D7's File into a "field_files" field. More on that in step 6.

Throw away your theme.My Drupal 5 site was a sub-theme of Zen. Not only is a D7 version of Zen not even out, but API differences in the theme layer make existing tpl.php files pretty much worthless. I started over with everything, including my CSS files but still copy/pasted as needed from my old theme.

Write a custom upgrade module.The biggest step in moving from D5 to D7 like this is getting everything that can't be rebuilt manually (like content, files, and comments) over to the new site. For this I wrote a Drupal 7 module that uses Batch API to migrate all my existing nodes and comments to the new site. The basic idea is that my Drupal 7 site would be set up with access to two databases in settings.php, "default" and "legacy". This module would manually select data from the D5 site, construct nodes, files, comments, etc. into objects and call node_save() on the Drupal 7 database. This lets Drupal worry about putting all the information into separate DB tables that the new Field module is prone to use (two database tables per field). I've attached the module I used to migrate my content to this article.

Rebuild your Views.If you've upgraded from Drupal 5 to 6 before, you're probably aware that the upgrade path from Views 1 (D5) to Views 2 (D6) was a crazy one. Essentially every view has to be rebuilt into Views 2, but Views provided a tool to help reconstuct these views. If you go straight from Views 1 to Views 3 (D7), don't expect this upgrade tool to work at all.

Work through the bugs.At this point, trying to use Drupal 7 for a project is still a dicey bet. The first problems I ran into was a conflict between Drupal core (RC2) and Views (dev branch). Apparently Drupal core changed an API post RC2 (surprise!) that caused Views arguments to break. The solution for me was to upgrade to both dev branches of Drupal 7 and Views 3. If you get stuck, search the issue queues of the module that is giving you trouble. Drupal 7 has a lot more early-adopters than previous versions of Drupal.

So there you go. I started upgrading last night and now I'm composing my first D7 entry on the live site. Obviously this approach requires a signficant amount of technical knowledge. Even for experienced Drupal developers, writing a Drupal 7 module that migrates all content can be an intimidating learning experience due to the extensive API changes from Drupal 6. Nonetheless I hope that my own experience in upgrading my relatively simple blog might help others in facing the challenging task of bridging 3+ years of Drupal advancement in a single sweep.

Attached is my module that provides the following:

A form at admin/content/custom-import for performing the import.

Migration of D5 Upload module files to a File module field named "field_files".

Migration from file paths to D7 stream wrapper paths (URIs).

Migration of D5 Nodes including converting "Stories" to "Articles", Upload to File fields, Link module to Text module, and Taxonomy terms into Term Reference Fields (note that I manually migrated Vocabularies and terms prior to creating the upgrade module).

Migration of D5 Comments to D7 Comments with the comment field as a D7 Field.

While all content is recreated on a clean Drupal site all nodes, files, and comments maintain their same identifiers.

Comments

Nice work. And thanks for the writeup.
I agree that skipping a major version of Drupal is a reasonable way to proceed. Folks should look at migrate.module to help them do that. It is a bit more structured than a one off import thats custom built. Migrate is what Economist, Examiner, etc. use. So it can handle big and little projects.

I've used Migrate a few different times and found that for the most part it's more learning than it's worth. Either approach (entirely custom or migrate) still requires coding. In D7 the new consistency in save functions (like comment_save()) make custom scripts even easier than before. Unless I'm doing a lot of migrations (or continuous pulling from another system), the custom approaches take me significantly less time. Not to mention I'm not dependent on the stability of other modules for my migration.

This helped me a lot. I've never used the batch API before and this code was great for me.
Looking at the example code, I think each batch method (i.e. custom_import_batch_comments) is only called once. When I'd expect it to be called multiple times. The $limit value is set, but never actually used in sql, so it runs over the full set.

From what I've read, the Migrate module makes it pretty easy to do imports with node / user references...since there's a bit of a chicken and egg problem with old nids / new nids. Is it worth it to adjust your code to implement something like this? Or do you think migrate is the way to go?