If one sticks too rigidly to one’s principles, one would hardly see anybody. (Agatha Christie)
Web applications are thin client (your browser) applications you access through an intranet or the Internet. Due to initial design of WWW, to create rich applications you need to deal with quite a large number of protocols/languages/concepts. To name a few; you need to know about JavaScript, CSS, HTML (preferably XHTML) and (possibly) JSON to write an AJAX application. This is only client side, the front end.
To preserve sanity, a layered approach that divides and isolates different parts of the application as much as possible is usually preferred. MVC pattern, which stands for Model-View-Controller, is a popular method for layering. Model is the layer where your data storage and manipulation occur. View is the layer where the presentation of model occur. The controller layer is basically the glue between both, where your so-called business rules should be. MVC pattern allows us to change the user interaction or to adapt different back ends with minimal modifications.[1]
I would like to summarize how a web application using MVC pattern works. But before that I need to state that this is an overly simplified model I will present.
- When an HTTP request comes to our server a
request object is created. This request object might have come modified according to our configuration before we have access to it. For example we might have wanted our request body to be decoded to unicode.
- The
request object is passed to a view function, depending on our configuration again. URL patterns might be matched against view functions or, rarely, against HTTP method (GET, POST, etc) or an entirely different method can be used to determine which view function to call.
- The
view function communicates with the model, adds, deletes or modifies the models as needed. Or it might just pull some data out and present it to user.
- We can generate the
response from our view but, it is preferred to use another template layer. Because that way you can seperate application logic and presentation logic. In that case the view function returns the necessary information into a template object.
- The
template object receives the data from the view and renders the final response (an HTML page, an XML document, etc).
We can pass data from view to template in a number of ways. We can use dictionaries (hashmaps) with arbitrary objects as values for example. When we return this dictionary from our view function the template objects picks it up and makes the data available by reference to its keys.
What we pass depends on the presentation logic in the template. Just to give you an idea, say we have a query page, where the user simply asks for a particular piece of data and we present it;
- It will surely need a reference to that data. (Maybe a model instance if you are using an ORM)
- It may present data conditionally, some parts might be available only to the administrators and invisible to normal users for example. So we would need to pass something to determine these conditions. I’ll come back to that something later.
- It may have other elements (on the page) that need references to other data. (There might me a dynamically generated menu on certain pages for instance)
This means we would pass three kinds of references to a template; primary data, conditinal information and secondary data. I have mentioned that when we have templates we can seperate application logic (business rules?) and presentation logic. In (beautiful) web framework Django‘s documentation it is said;
…the template system is meant to express presentation, not program logic.
and
We see a template system as a tool that controls presentation and presentation-related logic — and that’s it. The template system shouldn’t support functionality that goes beyond this basic goal.
If we wanted to put everything in templates, we’d be using PHP. Been there, done that, wised up.
I read presentation as laying out output and program logic as inserting/querying/modifying/deleting. This takes us back to the something I would like to discuss. How do we pass information related to conditional rendering. What we pass depends greatly on the functionality of the template engine. Let me illustrate with an example;
We want to display a tip if;
- A user who is registered for at least 2 weeks ago.
- Who has less than 5 friends.
- No other warning, such as a form validation error, is present on the page.
My intuition tells me I would need a reference to the list of errors and the (authenticated) user object in my template. I would like to have a reference to my user object because if I get anything less I might have to modify the view if the requirements change. So the registration date with the friend count would not do. After all we are deciding whether or not to show a tip, we are not doing inserting/querying/modifying/deleting I mentioned earlier.
Suppose we have user and error passed into our template, how do we use them; [2]
{% if user.registered.today().toordinal()
-user.registered.toordinal() >= 14
and user.friends.count < 5
and not errors %}
<div class="tip"> ... </div>
{% endif %}
Of course this does not work in Django. Django template engine’s if doesn’t have greater-than-or-equal operator, less-than operator and can not combine with truth testing (Instead has a seperate ifequal statement for truth testing). There are two workarounds;
We calculate conditions at the view and pass them as simple boolean values.
{% if user_registered_for_at_least_two_weeks
and user_has_less_than_five_friends
and no_errors %}
<div class="tip"> ... </div>
{% endif %}
We pass user reference and add checking functions to the models.
{% if user.registeredAtLeastTwoWeeksAgo
and user.hasLessThan5Friends %}
{% ifequal errors None %}
<div class="tip"> ... </div>
{% endif %}
{% endif %}
The first one is actually not a solution, since we calculate the conditions in the view the presentation code is spanning both layers. The second workaround allows us to decouple the template and the view but adding more and more functions on models for such simple operations is just displacing the problem.
As far as I understand Django template engine has very simple control flow functionality for a reason. To ensure logic stays in the view; limit functionality in the template (to truth testing and equality testing only). But eventually you want a somewhat complex template and either the view or the model gets cluttered.
The first sample, the one with the complex if, is jinja code. It’s conditional staments are almost as powerful as Python. This allows bad programming of course. You can do almost everything you are supposed to do in the view. But you if you follow decoupling principle you can have your code working, clean and maintanable.
I think Django is a special project (as an open source project, like Blender, like Inkscape in this sense). If we look carefully there are many lessons to be learned. Both in the API level and in the source code itself. I try to learn as much as possible from Django. But on my personal projects I would like to take full advantage of the expressive power of jinja (and sqlalchemy similarly). I try to assemble best components available, but I also do my best to follow best practises (learned from Django or other sources).
If you are considering Python for web development, I would say learn Django first. And then go ahead and study other frameworks and libraries. Python is extremely powerful and suits web development well. Once you found the setup (framework or whatever) that works best for you, I am sure you will enjoy it.
1: There are different interpretations of MVC model. What happens in each layer might slightly change depending on the application type and of course implementors.
2: I know these line breaks look stupid. But it blows the page away otherwise.
If one sticks too rigidly to one’s principles, one would hardly see anybody. (Agatha Christie) Web applications are thin client (your browser) applications you access through an intranet or the...