This is a small introduction to the django framework I did for fellow engineers at Anevia. It touches on many of the major concepts and core features of django and gives a typical workflow built about the example of a minimalist blog engine.
2. django : a short introduction
django is :
a python web framework
batteries included : ORM and DB migrations, template language,
cache, i18n, automatic admin pages, users and group management
DRY + KISS
If django doesn’t include a feature out of the box, it can be extended
with third-party extensions.
3. Workshop outline
We will discover some of the framework’s basics and see how to make a
blog engine with django :
MVT pattern and how django answers a request
designing the blog model entities
implementing some logic with views
displaying article and comments with templates
filling and validating forms
5. The MVT template
The Movel-View-Template pattern plays a central role in the
framework, every valid django application implements it.
6. Dive into models (1)
Model classes
Models are python classes that describe the database layout.
elements fetched from the database are instances of a model class
each attribute of the model represents a database column
An example with our blog’s Articles:
class Article(models.Model):
""" Blog article, has title, date, author and some content.
"""
title = models.CharField(’article title’, max_length=200)
author = models.ForeignKey(User)
pub_date = models.DateTimeField(’date published’)
content = models.TextField(’article content’)
Don’t forget : model classes must inherit from models.Model
7. Dive into models (2)
Models are ”synced” with the database, our Article class will generate
the following SQL :
CREATE TABLE "blog_article" (
"id" integer NOT NULL PRIMARY KEY,
"title" varchar(200) NOT NULL,
"author_id" integer NOT NULL REFERENCES "auth_user" ("id"),
"pub_date" datetime NOT NULL,
"content" text NOT NULL,
);
8. Take control with views
Views (classes or functions)
Views are the entry point of HTTP requests.
queries data from the DB, validates forms
returns rendered templates using collected data
def home(request):
""" Blog homepage.
"""
articles = Article.objects.all().order_by(’-pub_date’)
return render(request, ’blog_home.html’,
{
’articles’: articles,
})
9. Shape you centent with templates
Template files
Canvases that are used to generate pages with data provided by the
calling view
insert data, make DB queries (to some extent)
filters, tests, iterate over collections
template inheritance
{% extends "base.html" %}
{% block content %}
{% for article in articles %}
<div class="blog-post">
<h2>{{ article.title }} </h2>
<p>{{ article.pub_date }} by {{ article.author }} </p>
<p>{{ article.content }} </p>
</div>
{% endfor %}
{% endblock %}
10. Dispatching requests with patterns
url(r’^$’, ’blog.views.home’),
url(r’^article/(?P<article_id>w+)/$’, ’blog.views.view_article’),
13. First : let’s design the model
Weed need several entities to make a blog. Translating this
representation is straightforward.
Articles
Comments
Users
ArticleTags
14. Models (1/3) : Article
unique id (auto)
title
author (User object
provided)
publication date
content
tags
from django.contrib.auth.models import User
class Article(models.Model):
title = models.CharField(’article title’,
max_length=200)
author = models.ForeignKey(User)
pub_date = models.DateTimeField(
’date published’,
auto_now_add=True)
content = models.TextField(’content’)
tags = models.ManyToManyField(ArticleTag,
blank=True)
15. Models (2/3) : Comment
unique id (auto)
author name
parent Article
publication date
content
class Comment(models.Model):
author = models.CharField(’author name’,
max_length=100)
article = models.ForeignKey(Article)
pub_date = models.DateTimeField(
’date published’,
auto_now_add=True)
content = models.TextField(’comment’)
16. Models (3/3) : Article tag
unique id (auto)
tag name
class ArticleTag(models.Model):
tag_title = models.CharField(’tag title’,
max_length=30)
The article/tag relationship was added in the Article class, not need to
add it twice.
17. Model fields
integers
note = models.IntegerField(’just an int’)
number_nodes = models.PositiveIntegerField(’always be positive!’)
over9000 = models.BigIntegerField(’8 bytes integer’)
stings and text
title = models.CharField(’rather small string’, max_length=100)
content = models.TextField(’you can write your biography here’)
url = models.URLField(’some website’)
mailing = models.EmailField(’valid email address’)
time
birthday = models.DateField(’just the date’)
timestamp = models.DateTimeField(’down to microseconds’)
files
file = models.FileField(upload_to=’tmp_folder’)
lolcat = models.ImageField(upload_to=’img/cats/’)
18. Relationship fields
foreign key (many to one)
com_parent_article = models.ForeignKey(Article)
many to many
article_tags = models.ManyToManyField(ArticleTags)
one to one
secret_identity = models.OneToOneField(Superhero)
19. Smarter models
Validators can help keep logic inside models:
from django.core.exceptions import ValidationError
def loves_pizza(user):
if not user.loves_pizza:
raise ValidationError(u’How can one not love pizza?’)
class PizzaFanMembership(object):
user = models.ForeignKey(User, validators=[loves_pizza])
limit values range (kinda enum-like)
TOPPINGS = ( (0, ’none’),
(1, ’mushrooms’), (2, ’pepper’), ... )
pizza_topping = models.PositiveIntegerField(choices=TOPPINGS)
custom field types (eg. MarkdownTextField)
SQL-like constrains (not blank, defaults, etc.)
20. Back the blog : automatic admin pages !
Admin pages are generated from the model classes and give you complete
CRUD functions with no sweat :
22. Display data with views
Views (classes or functions)
Views are the entry point of HTTP requests.
queries data from the DB, validates forms
returns rendered templates using collected data
Roughly :
one page = one view
We will need 3 views for our blog :
”homepage” displaying all the articles (most recent first)
detailed view showing and article with all its comments
tag list page showing tag usage statistics
23. Homepage view
def home(request):
""" Blog homepage.
"""
articles = Article.objects.all().order_by(’-pub_date’)
return render(request, ’blog_home.html’,
{
’articles’: articles,
})
this view has no parameter other than the mandatory request
parameter
request constains a lot of useful stuff like sessions, POST, GET...
articles is a QuerySet and supports many SQL-like methods :
count, get, filter, exclude, contains, comparisons...
render generates the html page from a template and with a
context containing the articles QuerySet
24. Article view
def view_article(request, article_id):
""" View a specific article.
"""
article = get_object_or_404(Article, id=article_id)
comments = Comment.objects.filter(article=article).order_by(
’pub_date’)
return render(request, "blog_article.html",
{
’article’: article,
’comments’: comments,
})
get object or 404 is a shortcut function
notice how the comparison inside filter acts like a WHERE clause
oder by ’row’ is DESC, whereas -’row’ is ASC
a QuerySet can be a collection (comments) as well as a single item
(article)
25. Article view : which article by the way ?
Besides request, the view requires a article id parameter.
def view_article(request, article_id):
This id comes from the requested url, parsed by the url pattern :
url(r’^article/(?P<article_id>w+)/$’, ’blog.views.view_article’),
Mind that the parameter inside the url pattern and in the function
prototype must be the same !
Parameter’s order doesn’t matter though.
26. Beyond the render call
Template files
Canvases that are used to generate pages with data provided by the
calling view.
Templates can use data provided in the context dict, using item keys.
return render(request,
"blog_article.html",
{
’article’: article,
’comments’: comments,
})
In the blog article.html template,
we will be able to access :
article : the Article model
comments : the list of related
Comment objects
27. Templating (1)
insert values
<h2>{{ article.title }} </h2>
<p>{{ article.pub_date }} by {{ article.author }} </p>
use filters
{{ article.pub_date | date:"D d M Y" }}
{{ article.content | truncatewords:150 }}
{{ boolean | yesno:"yeah,no,maybe" }}
perform tests
{% if user.height < 1.5 %}
<p>You must be 1.5m to enter</p>
{% endif %}
loop over iterables
{% for comment in comments %}
<p>{{ comment.author }} : {{ comment.content }}
{% endfor %}
28. Templating (2)
template inheritance
<!-- base.html --> Some stuff around...
{% block footer %}
<!-- but no footer ! -->
{% endblock %}
<!-- other_page.html -->
{% extends base.html %}
{% block footer %}
Redefined footer ! This is fancier isn’t it ?
{% endblock %}
reverse urls (this is a killer feature !)
<a href="{% url ’blog.views.article’ article.id %} ">
Read the article {{ article.title }}
</a>
29. Example : complete article template
{% extends "base.html" %}
{% block content %}
<h2>{{ article.title }} </h2>
<p>{{ article.pub_date }} by {{ article.author }} </p>
<p>
{% for tag in article.tags.all %} {{ tag }} , {% endfor %}
</p>
<p>{{ article.content }} </p>
<hr>
<h2>Comments</h2>
{% for comment in comments %}
<div class="comment">
<h5>{{ comment.author }} </h5>
<p>{{ comment.pub_date }} </p>
<p>{{ comment.content }} </p>
</div>
{% endfor %}
{% endblock %}
31. Delete an article
Some views can be used to perform a specific task and then redirect.
def delete_article(request, article_id):
""" Delete an article given its ID.
"""
article = get_object_or_404(Article, id=article_id)
article.delete()
return redirect(’blog.views.home’)
Add an url pattern and it’s ready to use.
url(r’^delete/(?P<article_id>w+)/$’, ’blog.views.delete_article’),
<a href="{% url ’blog.views.delete_article’ article.id %} ">
Click to delete article {{ article }}
</a>
32. Form classes
To create new model objects or edit existing ones, we need to use Forms.
custom forms
class ContactForm(forms.Form):
sender = forms.EmailField()
message = forms.CharField()
models forms generated from your models
from .models import Article
class ArticleForm(forms.ModelForm):
""" Article creation or edition form.
"""
class Meta:
model = Article
fields = (’title’, ’content’)
Model forms use validation mechanisms defined inside the model.
33. Use a form to create an item
Forms are managed inside views.
def create_article(request):
""" Write a new blog article.
"""
edition_form = ArticleForm(request.POST or None)
if edition_form.is_valid():
# creating an article and setting its author
article = edition_form.save(commit=False)
article.author = request.user
article.save()
# redirect to the newly created article
return redirect(’blog.views.view_article’,
article_id=article.id)
# render the edition form if the data is blank or invalid
return render(request, "edit_article.html",
{
’form’: edition_form,
})
34. Display forms inside templates
the ”quick and dirty” way
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save</button>
<a class="btn btn-default" href="{% url ’blog.views.home’ %} ">
Cancel
</a>
</form>
custom field-by-field way
{% csrf_token %}
{% for field in form %}
<label for={{ field.auto_id }} >{{ field.label }} </label>
{{ field }}
{% endfor %}
35. Edit an existing item
The same ModelForm can be used for item edition :
def edit_article(request, article_id=None):
""" Edit a blog article.
"""
article = get_object_or_404(Article, id=article_id)
edition_form = ArticleForm(request.POST or None, instance=article)
if edition_form.is_valid():
# saving the edited article
edition_form.save()
return redirect(’blog.views.view_article’, article_id=article.id)
# render the edition form if the data is blank or invalid
return render(request, "edit_article.html",
{
’form’: edition_form,
})
37. But there’s more !
migration: migrates the database when model classes are modified
authentication: built-in user objects, groups, session management
protect your views
@permission_required(’blog.edit_comment’)
def moderation_view(request):
...
painless caching
cache views
@cache_page(60 * 15)
def view_with_many_queries(request):
...
cache template blocks
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
unit testing, XSS protection, flatpages...
38. NIH & DRY
If you’re in need for feature, first check if it not already available :
the framework has a lot of features and ready to use tools
if the framework doesn’t provide you with what you need, look at
the many great extensions available (image caching and resizing,
REST framework, LDAP, benchmarking. etc)
Django is all about keeping it simple, being productive while still making
reliable software.
39. Documentation & resources
official documentation : https://docs.djangoproject.com
Two scoops of django by Daniel Greenfeld and Audrey Roy