Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Django workshop : let's make a blog


Published on

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.

Published in: Engineering
  • Be the first to comment

Django workshop : let's make a blog

  1. 1. django workshop Pierre Sudron ( Anevia May 21, 2014
  2. 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. 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
  4. 4. Part I: django fundamentals
  5. 5. The MVT template The Movel-View-Template pattern plays a central role in the framework, every valid django application implements it.
  6. 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. 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. 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. 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 {{ }} </p> <p>{{ article.content }} </p> </div> {% endfor %} {% endblock %}
  10. 10. Dispatching requests with patterns url(r’^$’, ’blog.views.home’), url(r’^article/(?P<article_id>w+)/$’, ’blog.views.view_article’),
  11. 11. Request handling with django : the full cycle
  12. 12. Part II: let’s build a blog
  13. 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. 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. 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. 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. 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. 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. 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. 20. Back the blog : automatic admin pages ! Admin pages are generated from the model classes and give you complete CRUD functions with no sweat :
  21. 21. Part III: display our blog
  22. 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. 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. 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. 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. 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. 27. Templating (1) insert values <h2>{{ article.title }} </h2> <p>{{ article.pub_date }} by {{ }} </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.content }} {% endfor %}
  28. 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’ %} "> Read the article {{ article.title }} </a>
  29. 29. Example : complete article template {% extends "base.html" %} {% block content %} <h2>{{ article.title }} </h2> <p>{{ article.pub_date }} by {{ }} </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>{{ }} </h5> <p>{{ comment.pub_date }} </p> <p>{{ comment.content }} </p> </div> {% endfor %} {% endblock %}
  30. 30. Part III: add, modify and delete content
  31. 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’ %} "> Click to delete article {{ article }} </a>
  32. 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. 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 = = request.user # redirect to the newly created article return redirect(’blog.views.view_article’, # render the edition form if the data is blank or invalid return render(request, "edit_article.html", { ’form’: edition_form, })
  34. 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. 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 return redirect(’blog.views.view_article’, # render the edition form if the data is blank or invalid return render(request, "edit_article.html", { ’form’: edition_form, })
  36. 36. Part IV: what’s next?
  37. 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. 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. 39. Documentation & resources official documentation : Two scoops of django by Daniel Greenfeld and Audrey Roy
  40. 40. Thank you! Any questions?