Showing posts with label models. Show all posts
Showing posts with label models. Show all posts

August 14, 2009

Django Test Trick: Fixture Migration the Easy Way

This is not a way to do real model migration in the database. There are various projects underway for that; we had our own home rolled version, but I'm not in the mood to talk about it now.

However when you're running like mad you do probably keep changing what fields are in your models. This is 100% double plus true if you are running to keep up with a larger team of programmers who need your tool to configure their rapidly evolving applications.

And if you're running properly, you have a bunch of test fixture files which hold useful test data which probably won't even load when you're finished with today's changes.

You can hand-edit the fixtures, or write scripts to edit the fixtures, and I got pretty good at that. But sometimes you're adding new relations which are tricky enough that it the best tool to make a test fixture is the application you're already building. The trick is a simple two-stage process.

  1. When you add your new fields, you want to make them blank=True and null=True.
  2. Zero your database, load your old test fixture, which will load fine because it has no data and you demand none.
  3. Run around your test data doing the setup you need.
  4. Export your test data again.
  5. Go back to your models and remove the blank and null arguments.
  6. Now your models are as you want them, and your test data will load.
Also speaking from experience: do your field changes one or two at a time, and run your unit tests after each one. Nothing is worse than sorting through ten different model field changes causing errors at once.

Django Model Trick: UUIDs

One of the most important model tricks we used in our application was UUIDs. We were stuck on python 2.4 so we had to copy the UUID code in from later pythons, but now you can have it with even less work.

Django gives you automatically incrementing integer IDs for everything, which is possibly the worst ID scheme known to man. The only thing that recommends it is that integers are very small. But since everyone starts at the same place, they're only unique for one spin of the database... and that's not good enough.

In our configuration application model ID fields were something like:

uuid = models.CharField(max_length=36, primary_key=True,
default=make_uuid, editable=False)

where make_uuid is:

def make_uuid():
return str(uuid.uuid4())

What does this give you for your jumbo-size keys? Freedom to toss objects around like confetti!

If I make a test fixture, and my coworker makes a test fixture, and we want to load them on the same machine to run a joint test? There are no ID conflicts.

If I make a useful data setup and want to export it and give it to someone else, it will not overwrite the partial setup she already has.

You can also give me an object A, then later modify it to A' and send me that exported object... and when I import it, you know it will overwrite A because that's the ID that matches.

There are also some other nice features of UUID keys, for example: if I get the object ID for a foo when I wanted a bar, it won't resolve to a foo by coincidence because the foo and bar ID spaces are not going to overlap.

UUIDs - because your object IS a special snowflake.