Stubblog


Refactoring Django Apps Part 2, Unit Tests
February 20, 2010, 7:34 pm
Filed under: django | Tags: , ,

In the last post, I looked at moving all your data from one app to another, and today I’m going to go through using Django’s unit test extensions to test the app. This post also covers using signals to validate models that arn’t created from a form. You should of course also unit test the methods on your models outside of the views they’re called from, but there’s nothing special there, and there’s a whole bunch of blog posts that cover them.

The main aim of whos-playing.com is to make organising 5-a-side matches easy, so it’s main features are all around players saying they can or can’t make the game. A player can say they’ll play, but then change their mind, they can only play in matches for teams they are a member of, and they can only say THEY will play, not anyone else, the exception to this being the team organiser, he can add or remove any player. Lets look at the various things we might want to test.
Continue reading



Refactoring Django apps
February 8, 2010, 1:09 am
Filed under: django | Tags: ,

I’ve written a Django app to help people who organise social 5 a side games, (yeah, I know I said it was dead, but I left it up and it started to get a few users, it’s still not a success, but it is being used). It was the first “large” scale Django app I’d attempted and so it’s got a lot of things wrong with it, for starters I didn’t really see where I could split the project up into seperate Django apps, but I understand that a lot better now, so as I add new features I’m also splitting the project out into seperate apps. This blog post covers how I do it, and hopefully you’ll find it usefull.

I’m making a start from this post on Stackoverflow.

I’m already using south for migrations, so it makes sense to use that for refactoring too. If you havn’t already, install it and convert your app over.

Everything I created originally was under an app called, ‘sheets’, and now I want to split the parts of this that control creating and playing matches into a seperate app, which I’m going to call it ‘match’, don’t forget to add it to your INSTALLED_APPS

./manage.py startapp match

Now move the models over to your new models.py file, you’ll have to add a related_name parameter to some of your models if you’re using Postgres because south will try and create a relation with the same name as the existing models relation.

stuart-grimshaws-macbook:teamsheet stuartgrimshaw$ ./manage.py startmigration match --initial
Error: One or more models did not validate:
sheets.match: Accessor for field 'location' clashes with related field 'Location.match_set'. Add a related_name argument to the definition for 'location'.
sheets.matchplayers: Accessor for field 'player' clashes with related field 'User.matchplayers_set'. Add a related_name argument to the definition for 'player'.
sheets.sides: Accessor for field 'player' clashes with related field 'User.sides_set'. Add a related_name argument to the definition for 'player'.
sheets.sides: Accessor for field 'side' clashes with related field 'SideNames.sides_set'. Add a related_name argument to the definition for 'side'.
match.match: Accessor for field 'location' clashes with related field 'Location.match_set'. Add a related_name argument to the definition for 'location'.
match.matchplayers: Accessor for field 'player' clashes with related field 'User.matchplayers_set'. Add a related_name argument to the definition for 'player'.
match.sides: Accessor for field 'player' clashes with related field 'User.sides_set'. Add a related_name argument to the definition for 'player'.
match.sides: Accessor for field 'side' clashes with related field 'SideNames.sides_set'. Add a related_name argument to the definition for 'side'.

Don’t be tempted to dive right into your new migration and add your code to migrate data from the old models to the new ones, South recommends that you do a data migration in 3 steps, which makes sense when you consider that doing it in 1 big step might have it’s shortcomings.

The startmigration –initial command created the first migration, this simply created the tables in your database. Run migrate to make these alterations:

./manage.py migrate

Next, you need to create the migration that actually moves the data from one set of files to another:

./manage.py startmigration match sheets-to-match-data

The migration in the example on Stackoverflow is actually a lot more complicated than ours, we simply need to rename the database tables, like this:

def forwards(self, orm):
  db.rename_table('sheets_match', 'match_match')
  db.rename_table('sheets_matchplayers', 'match_matchplayers')
  db.rename_table('sheets_sides', 'match_sides')

def backwards(self, orm):
  db.rename_table('match_match', 'sheets_match')
  db.rename_table('match_matchplayers', 'sides_matchplayers')
  db.rename_table('match_sides', 'sides_sides')

So, that’s the data migrated over, now we need to migrate any views to the new app, just identify and copy them, one of the mistakes I made in the original attempt actually helps me out here, I created a seperate urls.py for some sections, that was my first clue I was doing something wrong.

After copying this file over to the new apps urls.py, it’s time to take the views it mentions and copy them to the new apps views file, once you’re happy that they’re working, you can delete them from the old views file too.

How do you know they’re working? Well, you have been using Django’s unit tests havn’t you? No? Well that’s another post then.