django workshop : let's make a blog

40
django workshop Pierre Sudron ([email protected]) Anevia May 21, 2014

Upload: pierre-sudron

Post on 17-Dec-2014

200 views

Category:

Engineering


1 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: Django workshop : let's make a blog

django workshop

Pierre Sudron ([email protected])

Anevia

May 21, 2014

Page 2: Django workshop : let's make a blog

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 extendedwith third-party extensions.

Page 3: Django workshop : let's make a blog

Workshop outline

We will discover some of the framework’s basics and see how to make ablog 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

Page 4: Django workshop : let's make a blog

Part I:django fundamentals

Page 5: Django workshop : let's make a blog

The MVT template

The Movel-View-Template pattern plays a central role in theframework, every valid django application implements it.

Page 6: Django workshop : let's make a blog

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

Page 7: Django workshop : let's make a blog

Dive into models (2)

Models are ”synced” with the database, our Article class will generatethe 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,

);

Page 8: Django workshop : let's make a blog

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,

})

Page 9: Django workshop : let's make a blog

Shape you centent with templates

Template files

Canvases that are used to generate pages with data provided by thecalling 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 %}

Page 10: Django workshop : let's make a blog

Dispatching requests with patterns

url(r’^$’, ’blog.views.home’),

url(r’^article/(?P<article_id>\w+)/$’, ’blog.views.view_article’),

Page 11: Django workshop : let's make a blog

Request handling with django : the full cycle

Page 12: Django workshop : let's make a blog

Part II:let’s build a blog

Page 13: Django workshop : let's make a blog

First : let’s design the model

Weed need several entities to make a blog. Translating thisrepresentation is straightforward.

Articles

Comments

Users

ArticleTags

Page 14: Django workshop : let's make a blog

Models (1/3) : Article

unique id (auto)

title

author (User objectprovided)

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)

Page 15: Django workshop : let's make a blog

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’)

Page 16: Django workshop : let's make a blog

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 toadd it twice.

Page 17: Django workshop : let's make a blog

Model fields

integersnote = models.IntegerField(’just an int’)

number_nodes = models.PositiveIntegerField(’always be positive!’)

over9000 = models.BigIntegerField(’8 bytes integer’)

stings and texttitle = 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’)

timebirthday = models.DateField(’just the date’)

timestamp = models.DateTimeField(’down to microseconds’)

filesfile = models.FileField(upload_to=’tmp_folder’)

lolcat = models.ImageField(upload_to=’img/cats/’)

Page 18: Django workshop : let's make a blog

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)

Page 19: Django workshop : let's make a blog

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.)

Page 20: Django workshop : let's make a blog

Back the blog : automatic admin pages !

Admin pages are generated from the model classes and give you completeCRUD functions with no sweat :

Page 21: Django workshop : let's make a blog

Part III:display our blog

Page 22: Django workshop : let's make a blog

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

Page 23: Django workshop : let's make a blog

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 acontext containing the articles QuerySet

Page 24: Django workshop : let's make a blog

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)

Page 25: Django workshop : let's make a blog

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 functionprototype must be the same !Parameter’s order doesn’t matter though.

Page 26: Django workshop : let's make a blog

Beyond the render call

Template files

Canvases that are used to generate pages with data provided by thecalling 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 relatedComment objects

Page 27: Django workshop : let's make a blog

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 %}

Page 28: Django workshop : let's make a blog

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>

Page 29: Django workshop : let's make a blog

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 %}

Page 30: Django workshop : let's make a blog

Part III:add, modify and delete content

Page 31: Django workshop : let's make a blog

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>

Page 32: Django workshop : let's make a blog

Form classes

To create new model objects or edit existing ones, we need to use Forms.

custom formsclass ContactForm(forms.Form):

sender = forms.EmailField()

message = forms.CharField()

models forms generated from your modelsfrom .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.

Page 33: Django workshop : let's make a blog

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,

})

Page 34: Django workshop : let's make a blog

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 %}

Page 35: Django workshop : let's make a blog

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,

})

Page 36: Django workshop : let's make a blog

Part IV:what’s next?

Page 37: Django workshop : let's make a blog

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

Page 38: Django workshop : let's make a blog

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 atthe many great extensions available (image caching and resizing,REST framework, LDAP, benchmarking. etc)

Django is all about keeping it simple, being productive while still makingreliable software.

Page 39: Django workshop : let's make a blog

Documentation & resources

official documentation : https://docs.djangoproject.com

Two scoops of django by Daniel Greenfeld and Audrey Roy

Page 40: Django workshop : let's make a blog

Thank you!Any questions?