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
-~----------~----~----~----~------~----~------~--~---

Reply via email to