> besides the testing issues (which are certainly a heated debate!), i have to 
> say that my Django projects became far better organized and a lot more 
> flexible when i learned to put most of the code on the models, and not on the 
> views.

This is a pretty interesting topic.  Coming from a PHP background, I
started out with very messy code (each .php file contained it's own
program logic at the top).  From there I went to Javabeans (tomcat)
and got a crash course in MVC & OO web apps, almost to an extreme I
wouldn't want to duplicate.  Now that I'm in django, I have a nice
middle ground.

There are certain ideals (ie. programming paradigms) that might make
unit testing easier.  One is separating the different parts of MVC
(model, view, controller).  Controller in our case is mostly the
django code (if I'm not wrong).  The views are our views.py
functions.  The models are our models.py file.  But there's the
unspoken part of business logic.  By default, most django apps seem to
have very dumb models.py files (the classes are simply wrappers around
database rows & tables), and all the logic is in views.py.  However,
the business logic should really be pulled out of views.py, whether
that goes into heavy models.py objects (like Javier has), or a
separate business_logic.py file.

It helps to keep asking yourself, what is this line of code doing?

For example, in views.py:

    def handle_add_item_form(request):
        form = AddItemForm(request.POST)
        if form.is_valid():
            item = Item.objects.create(
                name=form.cleaned_data['name'],
                price=form.cleaned_data['price'],
                status=form.cleaned_data['status'],
            )
            if item.status == 'out of stock':
                # Order 100 more items for later
                order_more_items(item, 100)
                email_admin_about_oos(settings.ADMINS)

This handles a simple form to create an item.  However, there's
clearly business logic here... the whole "status == 'out of stock'"
sticks out like a sore thumb.  It'd be better like this:

in views.py:

    def handle_add_item_form(request):
        form = AddItemForm(request.POST)
        if form.is_valid():
            item = ItemManager.create(
                name=form.cleaned_data['name'],
                price=form.cleaned_data['price'],
                status=form.cleaned_data['status'],
            )

and in managers.py:

    class ItemManager:
        def create(name, price, status):
            item = Item.objects.create(
                name=form.cleaned_data['name'],
                price=form.cleaned_data['price'],
                status=form.cleaned_data['status'],
            )
            if item.status == 'out of stock':
                # Order 100 more items for later
                order_more_items(item, 100)
                email_admin_about_oos(settings.ADMINS)

You have to type a bit more (to create all those managers, or service
objects, or whatever you call them), but the benefit is that business
logic is now in one place.  Views.py should only be the dumb glue that
connects the HTTP requests with the service objects, and returns the
results.

Like Daniel said, models.py are usually so simple/dumb that you don't
need to test them much.  Your views.py should be the same way.
They're simply glue, so the only things that will go wrong are simple
logic errors and typos.  All the complicated stuff is in the business
logic objects (the managers.py here), and that's what you spend a lot
of time unit testing.

In this example, you can use the ItemManager.create() function
anywhere you create items... adding new ones, copying existing ones,
importing items from a file, etc.  In the old example, you'd have to
duplicate the views.py code in each view that handles this situation
and test each one.  You could put all these functions into the
models.py file if you wanted, you'd just end up with huge models.py
objects, and you might also run into cases where there is not an
obvious place to put a bit of business logic.  Not every business
logic function corresponds exactly to one model.

Another great change... with all your logic separated like this, if
you have to change something very complicated (like the way the whole
system handles out of stock, ordering more inventory, permissions,
etc), you have one place to change & test.  It's great.

So do I use this?  No, all my code is in wrapped up in my views.py and
I have no unit testing.  I try to break out duplicated functionality
into their own functions, but that's more for convenience than true
separation.  I'd recommend starting with the "keep them separated"
mentality, since it gets harder and harder as a project goes on.

Sorry for the long rant, but it touched on something I've been
observing in my own code.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to