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