Hi All, I'd like to introduce a little project I've been working on with Kumar McMillan of farmdev.com. He's the author of the fixture<http://farmdev.com/projects/fixture/>library which allows a declarative approach to writing database fixtures. I've written django support for it so that you can use it to test django models. The stuff outlined below is in my bitbicket mirror<http://bitbucket.org/boothead/fixture-django/>but should be folded into fixture (the easy_installable version) soon.
Motivation: ------------------------- We've got a pretty big django app, and we've found django's fixtures to be a little high in maintenance cost... Consider an app that is central to a lot of other related apps: In our case we have the concept of a business Network, that has members, a blog, forums documents etc etc and a Profile which has contacts, an account and a relationship to auth.User. To do meaningful testing almost all of the fixtures will have to include some content from the app that contains the Network model and the app that contains the Profile model. Now if one of the models in either of these apps changes in structure, then all of the tests that have dumped data from that model, which will be most of them, will be broken and you'll have to go through all fixtures and make the required changes. Fixture --------------- Given that the structure of our app changes reasonably regularly and that repetition can lead to rocking back and forth with unfocussed eyes, I started to look for "Another Way". I found fixture in which you declaratively write DataSets<http://farmdev.com/projects/fixture/using-dataset.html>and then load them into the database. These DataSets can relate to each other using standard python syntax so each app can define example fixtures and other fixtures can import those and declare relations (you can also subclass rows within the DataSet from each other too). So now when the schema changes you'll only need to change it on one place and all your tests should work again. Example ----------------- Given the following models: class Author(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) def __unicode__(self): return u"%s %s" % (self.first_name, self.last_name) class Book(models.Model): title = models.CharField(max_length=10) author = models.ForeignKey(Author, related_name='books') def __unicode__(self): return u"%s, %s" % (self.title, self.author) class Reviewer(models.Model): name = models.CharField(max_length=100) reviewed = models.ManyToManyField(Book, related_name='reviewers') def __unicode__(self): return u"%s" % (self.name) A fixture would look like: from fixture import DataSet class app__Author(DataSet): class frank_herbert: first_name = "Frank" last_name = "Herbert" class guido: first_name = "Guido" last_name = "Van rossum" class app__Book(DataSet): class dune: title = "Dune" author = app__Author.frank_herbert class python: title = 'Python' author = app__Author.guido class app__Reviewer(DataSet): class ben: name = 'ben' reviewed = [app__Book.dune, app__Book.python] A doctest would look something like this: >>> from fixture import DjangoFixture >>> from django.db.models.loading import get_model, get_app >>> from fixture.test.test_loadable.test_django.util import assert_empty >>> app = get_app('app') >>> assert_empty(app) # This just checks that model.objects.count() == 0 for >>> all models in app >>> Book = get_model('app', 'Book') >>> django_fixture = DjangoFixture() >>> data = django_fixture.data(app__Book) >>> data.setup() All the books are here: >>> Book.objects.all() [<Book: Dune, Frank Herbert>, <Book: Python, Guido Van rossum> And fixture has pulled in all the Authors too, even though only the Book fixture was explicitly loaded: >>> Author = get_model('app', 'Author') >>> Author.objects.all() [<Author: Frank Herbert>, <Author: Guido Van rossum>] But not the Reviewers: >>> get_model('app', 'Reviewer').objects.count() 0 If we load the app__Reviewers DataSet, all of the others will be pulled in: >>> data.teardown() # Get rid of the old data >>> assert_empty(app) >>> data = django_fixture.data(app__Reviewer) >>> data.setup() >>> get_model('app', 'Reviewer').objects.count() 1 >>> Book.objects.count() 2 >>> Author.objects.count() 2 >>> data.teardown() I'd like to extend the work I've done to allow serialization of existing QuerySets to DataSet objects, and better integration with django's test framework. So I have a few questions on where to go next... 1. What's the best way to integrate with django's test framework (Transactions, the test client etc). Subclass django's TestCase or something else? 2. I've written some validation code, I'd appreciate it if someone could have a look through this module<http://bitbucket.org/boothead/fixture-django/src/tip/fixture/loadable/django_loadable.py>particularly the field_is_required function and the _check_schema method and tell me if there's a better way to do it. 3. At the moment you can only specify a relationship from the direction that the field is defined. i.e as we have Book -> fk -> Author you can't write a dataset the does: class app__Author(DataSet): class auth1: ... books = [app__Book.book1, ...] I made it work this way to simplify development, but I'm wondering if it's the right decisoin. Is this desirable behaviour? I hope this is usefull to everyone, and in the spirit of Malcolms recent blog post, a big thank you to all of you :-). If you have any improvement suggestions please let me know too. Cheers, Ben -- Regards, Ben Ford ben.for...@gmail.com +447792598685 --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-developers?hl=en -~----------~----~----~----~------~----~------~--~---