home icon contact icon rss icon

The advantages of MVC

MVC stands for “model-view-controller”, a programming paradigm where the application is split into three interconnected yet disparate parts (as interpreted by Django):
  1. the model: the data of the application
  2. the view: the presentation and selection of data to present to the user
  3. the controller: the brains of the application that connects the user to the correct view

Note, the MVC paradigm is interpreted differently by the popular “Rails” framework (used for this very blog!). In Rails there are multiple controllers that decide what data to present to the user, and the view is left to simply display the data. In this interpretation Django’s views would be seen as controllers, and Django’s templates would be views.

To best describe how MVC helps us do our jobs as web programmers better, lets look at a simple PHP webpage application written as a script:


<?php

$page_title = "Ten most recent blog entries";

include_once($include_path.'/header.php');

// setup $db
include_once($include_path.'/db_connection_for_this_application.php');

$recent_blog_posts = mysql_query("SELECT 
  posts.post_date, 
  posts.post_title, 
  posts.post_content 
FROM 
  posts
WHERE 
  posts.post_status='publish'
ORDER BY posts.post_date DESC LIMIT 10, $db);

echo '<h1>Recent Posts</h1>';
while($posts_data = mysql_fetch_array($recent_blog_posts)) {
  echo '<div class="post">';
  echo '<div class="blog_date">' . $posts_data['post_date'] . "</div>\n";
  echo '<div class="blog_entry_title">' . $posts_data['post_title'] . "</div>\n";
  echo '<div class="blog_entry">' . $posts_data['post_content'] . "</div>\n";
  echo '</div>';
}

include_once($include_path.'/footer.php');

?>

Simple enough. That’s actually the big advantage of writing web applications as scripts. They are (almost) entirely self-contained and easy to understand. You start reading at the top and finish at the bottom, with no file jumping or object modeling required.

Unfortunately, this simpliciy is also very limiting:
  • The content and the programming are combined. This means that, aside from CSS, if any web designers want to update the look of the page then they’ll have to modify the same file that contains the application. That’s a great opportunity for things to go wrong.
  • The header and footer are fixed. This means altering something like the title of the page forces the application to declare prespecified variables (e.g. the $page_title), that the header file assumes are in place. A bunch of these variables can quickly clutter an application. At a simple level, including headers and footers makes sense, but as pages get more complex they get harder to maintain. What if you have a standard header/footer, but want slightly modified versions for other sections? Using header/footer includes means that you’ll have to create separate include files for each. Without a clear organizational structure things can get confusing pretty quickly.
  • Low-level programming is involved. The programmer has to build database queries by hand, which is a great place for bugs to hide.
  • No object oriented programming. You can’t wrap up functions and data into logical blocks with specific interfaces (e.g. objects). In a better system, we’d just ask the “Blog” object directly for the ten most recent posts with something like {{ blog.posts | ‘limit’: 10 }}

As you can see, there is room for something better. That something is an MVC framework such as Django or Rails. Let’s look at how one (Django) solves the problems with our PHP script. I’ll show you the code first, then get into the explanation:


### models.py (the *model*) ###

from django.db import models

class Blog(models.Model):
    entry_title = models.CharField(maxlength=50)
    blog_date = models.DateField()
    entry = models.TextField()

### views.py (the *view*) ###

from django.shortcuts import render_to_response
from models import Blog

def recent_posts(request):
    post_list = Blog.objects.order_by('-blog_date')[:10]
    return render_to_response('recent_posts.html', {'post_list': post_list})

### urls.py (the *controller*) ###

from django.conf.urls.defaults import *
import views

urlpatterns = patterns('',
    (r'^recent/$', views.recent_posts),
)

### recent_posts.html (the *template* (part of the view))###

<html><head><title>Recent Blog Posts</title></head>
<body>
<h1>Recent Posts</h1>
{% for post in post_list %}
<div class="post">
  <div class="blog_date">{{blog_date}}</div>
  <div class="blog_entry_title">{{entry title}}</div>
  <div class="blog_entry">{{entry}}</div>
{% endfor %}
</body></html>

Ok! That seems a little confusing compared to the script doesn’t it? Let’s dive in:

recent_posts.html

Take a closer look at the “recent_posts.html”. Think of how lucidly clear that file is to a web designer? There’s hardly any code! While of course no templating language can eliminate programming constructs Django’s system does a great job of only giving the template designers what they need to get the job done. The template “programming” is using its own programming language, not straight Python. There are two big reasons for this: 1) the language is meant to be simple and only provide the features needed to select or present the proper data, 2) the language doesn’t allow templates to do anything that the application should handle (e.g. a template can’t tell the database to delete itself).

Yes, my example doesn’t demonstrate how Django helps with header/footer includes, that’s coming up so be patient!

models.py

This is simply describing to Django how to organize the data object that we’ll be dealing with.

urls.py

This is routing a user’s request for the web address ‘recent/’ (e.g. mysite.com/recent) to the recent_posts function in views.py.

views.py

This is the best part, take a look again:


def recent_posts(request):
    post_list = Blog.objects.order_by('-blog_date')[:10]
    return render_to_response('recent_posts.html', {'post_list': post_list})

That’s it? That’s it! Our complex SQL query is now a higher level abstraction where we tell Django to tell the blog to give us the latest ten posts. Done!

Now lets look at that last piece: header/footer files.

Django does allow us to build templates that pull in header/footer includes, but it also lets us solve the common data problem with template inheritance. With template inheritance we don’t abstract the pieces of our web pages that are the same, we pull out the pieces that are different. Let me explain with a very simple webpage:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>My Website</title>
</head>
<body>
    <h1>Page Title</h1>
    <p>Customized content!</p>

    <hr>
    <p>This is my footer</p>
</body>
</html>

Done using header/footer includes:


### header include file ###
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>My Website</title>
</head>
<body>

### footer include file ###
<p>This is my footer</p>
</body>
</html>

### custom page ###
include(header_file)

<h1>Page Title</h1>
<p>Customized content!</p>

include(footer_file)

Now done with Django’s template inheritence:


### base.html ###
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
    <h1>{% block pagetitle %}{% endblock %}</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <p>This is my footer.</p>
    {% endblock %}
</body>
</html>

### page.html ###
{% extends "base.html" %}

{% block title %}Customized Title!{% endblock %}

{% block pagetitle %}Customized page title!{% endblock %}
{% block content %}
<p>Customized content!</p>
{% endblock %}

See what we did there? The parts of the page that will change are pulled out of the base. Django’s template system lets you declare “blocks” that indicate that that part of the page may change. If an extending template declares a block that matches that of the parent template, then the parent’s block is overwritten. If a block isn’t declared by the sub-page, that is in the parent page then the parent page’s block is used. Notice in our “page.html” the footer isn’t discussed at all. Django sees that we are “extending” base.html and that we haven’t specified our own footer block so it defaults to the footer block that was in base.html.

Perfect! We can create base templates that allow for child pages to overwrite any part that we explicitly allow! No more locked headers or footers!

Templates can go through multiple levels of inheritence as well. We could easily have a base template that is then extended into “section” templates that are then extended to specific pages. With a clear and logical chain of command it is easy to see how the webpage templates work together. Much better than a couple dozen header/footer files lying around. We get all the advantages of reusing designs without the headaches!

So there you have it. Sorry for the rambling/unedited nature of this post. It’s 2:30am, I can’t sleep, but I felt like sharing.

Minnie Elliott said

Nov 12, 2008 @ 07:04 PM

e8myzdwqspi211ca

RSS feed for comments on this post

Leave a Comment