Python & Django for PHP Refugees

Jamie Matthews

Brighton & Hove Python User Group

12th April 2011

About me

  • Used both Python and PHP (amongst other things) for ~8 years, on and off.
  • Web freelancing using PHP while at uni.
  • Python for MSc work: genetic algorithms, gluing together C code.
  • ~3 years full-time PHP developer.
  • Co-founded DabApps, using Python and Django.

Tesco Cars

Outline

  • PHP ain't so bad
  • PHP and Python: compare and contrast
  • PHP and Django: views, templates, models, other goodies
  • Demo time
  • Homework

Elephant

PHP ain't so bad

  • PHP is a programming language and a web framework and a templating language.
  • PHP is always going to be easier to deploy than Python (thanks to mod_php).
  • It is perfectly possible to write good code in PHP. You just have to be very careful.

For beginners

  • Python is easier to learn than PHP.
  • Web programming with PHP is easier to learn than Python and Django.
  • ...but, web programming isn't meant to be easy. Web applications are complex.
  • Most important: get your head around the concepts. The details come with experience.

Compare

  • Both are interpreted, high-level languages with dynamic typing.
  • Both are multiparadigm languages. Support procedural, object-oriented and functional styles.

PHP

1 <?php
2 items = array('spam', 'eggs', 'beans');
3 foreach ($items as $item) {
4     echo $item . "\n";
5 }
6 ?>

Python

1 items = ['spam', 'eggs', 'beans']
2 for item in items:
3     print item
  • WTF? No semicolons? No curly braces?
  • OMG! Significant whitespace!

Conventions & Consistency

PHP

  • CamelCase
  • lowercase_with_underscores
  • something_Else_Weird($MY_VAR);
  • Intentation: tabs? Two spaces? Four spaces?

Python

PEP8: Style Guide for Python Code

http://www.python.org/dev/peps/pep-0008/

No arguments.

Namespacing

PHP < 5.3

...what namespaces?

Everything is global. The only way to namespace code is to use a class.

PHP >=5.3

Namespaces are optional. No namespace declaration? No namespace.

Python

Namespaces are one honking great idea -- let's do more of those!

A file containing Python code (a module) gets its own namespace.

firstfile.py

1 some_variable = "Hello Brighton"

secondfile.py

1 from firstfile import some_variable
2 print some_variable

Everything is an object

Strings are objects

1 my_string = 'hello'
2 print my_string
3 print my_string.upper()
4 print 'hello'.upper()

Functions are objects

1 def say_hello(name):
2     print 'hello ' + name
3 
4 def say_goodbye(name):
5     print 'goodbye ' + name
6 
7 greetings = [hello, goodbye]
8 for greeting in greetings:
9     print greeting('brighton')

Classes are objects

... but let's not go there.

Python global built-in functions: 74

Python built-in functions

PHP global built-in functions

PHP built-in functions

PHP global built-in functions (2)

PHP built-in functions

PHP global built-in functions (3)

PHP built-in functions

PHP global built-in functions: 5845

PHP built-in functions

Personal favourite

date sunrise

This is not good language design.

Pony

MCV

MTV

Code and URLs: PHP

In PHP, it's tempting to use Apache and the filesystem to map a URL to some code.

This goes in myfile.php

1 <h1><?php echo "Hello!"; ?></h1>

Your URL becomes http://www.yourserver.com/myfile.php

Problems

  • Code becomes a tangled mess of include "header.php";
  • Your URLs are ugly.

Code and URLs: Django

All requests get sent to the framework. It is responsible for routing URLs to pieces of code.

Functions that handle requests and return responses are called views (other frameworks might call a similar concept controllers).

views.py:

1 from django.http import HttpResponse
2 
3 def my_view(request):
4     return HttpResponse('<h1>Hello!</h1>')

urls.py:

1 from django.conf.urls import patterns, url
2 from views import my_view
3 
4 urlpatterns = patterns(
5     url(r'^$', my_view),
6 )

Templates: PHP

PHP is its own templating language.

 1 <?php
 2     $title = "Saying hello!";
 3     $names = array("Rod", "Jane", "Freddy");
 4 ?>
 5 
 6 <h1><?php echo $title; ?></h1>
 7 <ul>
 8 <?php foreach ($name as $name): ?>
 9     <li><?php echo $name; ?></li>
10 <?php endforeach; ?>
11 </ul>

With great power comes great responsibility. Most developers are lazy.

Ever seen a file with HTML, CSS, JavaScript, PHP and SQL in it? Me too.

Templates: Django

views.py:

1 from django.shortcuts import render
2 
3 def my_view(request):
4     return render(request, 'mytemplate.html', {
5         'title': "Saying hello!",
6         'names': ['Rod', 'Jane', 'Freddy'],
7     })

templates/mytemplate.html:

1 <h1>{{ title }}</h1>
2 <ul>
3 {% for name in names %}
4     <li>{{ name }}</li>
5 {% endfor %}
6 </ul>

Can add power through tags and filters. Many built in, can define your own.

1 {{ value|date:"D d M Y" }}

Models

Many PHP frameworks provide an object-relational mapper. So does Django.

... but Django's is nicer.

  • Django considers your code to be the canonical representation of the structure of your data.
  • Django generates the necessary create table statements to set up your database schema.

(As far as I know, no PHP ORMs do this. I wish they would).

Supports MySQL, PostgreSQL, SQLite and Oracle out of the box. Third-party backends available (Sybase, IBM DB2, Microsoft SQL Server, Firebird, ODBC...)

Model fields

  • Fields can map directly onto database types (CharField or IntegerField) or be rich objects with custom validation rules (URLField, ImageField).
  • Fields can represent relationships: ForeignKey, ManyToManyField, OneToOneField.

Declarative API

1 from django.db import models
2 
3 class Note(models.Model):
4     title = models.CharField(max_length=50)
5     content = models.TextField()
6     author_name = models.CharField(max_length=100)
7     created_at = models.DateTimeField(auto_now_add=True)

This is impossible in PHP (please prove me wrong!)

Making queries

 1 from models import Note
 2 
 3 first_note = Note.objects.get(pk=1)
 4 
 5 all_notes = Note.objects.all()
 6 
 7 notes_by_jamie = Note.objects.filter(author_name='Jamie')
 8 
 9 first_note_by_jamie = notes_by_jamie[0]
10 
11 new_note = Note()
12 new_note.author_name = 'Jamie'
13 new_note.title = 'My new note'
14 new_note.content = 'Content of my new note'
15 new_note.save()

Other goodies: free admin interface

Just by defining your models, you get a rich editing interface for free.

admin

Other goodies: forms

Django lets you create HTML forms in a high-level object-oriented way.

Similar declarative API to models:

1 from django import forms
2 
3 class ContactForm(forms.Form):
4     name = forms.CharField()
5     email_address = forms.EmailField()
6     message = forms.CharField(widget=forms.Textarea)

Then, in your template...

1 {{ form.as_p }}

Generates...

1 <p>
2     <label for="id_name">Name:</label>
3     <input type="text" name="name" id="id_name" />
4 </p>
5 <p>
6     <label for="id_email_address">
7 
8 ... etc

Other goodies: form validation

Built-in form fields know how to validate themselves (EmailField checks for a valid email address, etc).

You can specify your own validation rules, on a per-field and/or per-form basis.

1 from forms import ContactForm
2 
3 def my_view(request):
4     if request.method == 'POST':
5         form = ContactForm(request.POST)
6         if form.is_valid():
7             # process form, send email etc..

You can also use a ModelForm to create a form based on a model. No need to define the form fields twice (this is how the automatic admin works).

Other goodies

  • Authentication
  • Cache system
  • Geospatial database extensions
  • Internationalisation, localisation
  • Testing
  • Signals
  • Generic views

and, of course

  • Documentation
  • Huge community
  • Open source culture
  • Jazz

Demo time

Homework

Read up on:

  • pip
  • virtualenv
  • bpython
  • gunicorn

The Zen of Python

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!