Dev Week -- Developing websites with Django -- lukasz and stuartm -- Wed Sep 2nd, 2009


(01:00:42 PM) lukasz: Hi everybody, my name is Łukasz Czyżykowski. I work for ISD (Infrastructure Systems Development) team at Canonical. Me and my colleague Anthony Lenton (achuni) will be talking about developing web sites with Django.
(01:00:49 PM) achuni: that's me.  hi, I'm Anthony Lenton and I also work at ISD.
(01:00:54 PM) achuni: this talk is going to be generally given by Łukasz.
(01:00:55 PM) achuni: I'm going to be here to answer questions, and maybe interrupt Łukasz just to bother.
(01:01:11 PM) lukasz: For the purpose of this tutorial we'll build simple web application, we'll use most bits of Django. Our app will be partial Twitter/ clone.
(01:01:30 PM) lukasz: All code for this project is accessible at, you can either download it and look at revisions which moves app forward in the same way as this session is planned.
(01:02:13 PM) lukasz: or only follow irc session as all required code will be presented here
(01:02:27 PM) lukasz: I assume that everybody is using Jaunty and have Django installed. If you still don't have it:
(01:02:27 PM) lukasz: $ sudo apt-get install python-django
(01:02:27 PM) lukasz: will do the trick.
(01:02:50 PM) lukasz: First step is to create Django project:
(01:03:00 PM) achuni: (as usual, or for if you've just arrived, if you have questions, shout them on #ubuntu-classroom-chat)
(01:03:18 PM) lukasz: $ django-admin startproject twitbuntu
(01:03:18 PM) lukasz: $ cd twitbuntu
(01:04:04 PM) lukasz: Project is container for database connection settings, your web server and stuff like that.
(01:04:26 PM) lukasz: Now twitbuntu contains some basic files:
(01:04:35 PM) lukasz: - you'll use this script to invoke various Django commands on this project,
(01:04:44 PM) lukasz: - here are all settings connected to your project,
(01:04:55 PM) lukasz: - mapping between urls of your application and Python code, either created by you or already existing.
(01:05:09 PM) lukasz: - which marks this directory as Python package
(01:05:26 PM) lukasz: Next we'll setup database connection
(01:05:35 PM) lukasz: Open file in your favourite text editor.
(01:05:52 PM) lukasz: For purpose of this tutorial we'll use very simple sqlite database, it holds all of its data in one file and doesn't require any fancy setup. Django can of course utilise other databases, MySQL and PostgreSQL being most popular choices.
(01:06:18 PM) lukasz: Enter sqlite3 in DATABASE_ENGINE setting. Line should look like that:
(01:06:18 PM) lukasz: DATABASE_ENGINE = 'sqlite3'
(01:06:18 PM) lukasz:  
(01:06:29 PM) lukasz: Also set file name in DATABASE_NAME to db.sqlite (it can be whatever you like):
(01:06:29 PM) lukasz: DATABASE_NAME = 'db.sqlite'
(01:06:54 PM) lukasz: To test that those settings are correct we'll issue syncdb management command. It creates any missing tables in the database which in our case is exactly what we want to get:
(01:06:54 PM) lukasz: $ ./ syncdb
(01:07:17 PM) lukasz: If everything went right you should see bunch of "Creating table" messages and query about creating superuser. We want to be able to administer our own application so it's good to create one. Answer yes to first question and proceed with other questions
(01:07:34 PM) lukasz: My answers to those questions are:
(01:07:40 PM) lukasz: Would you like to create one now? (yes/no): yes
(01:07:40 PM) lukasz: Username (Leave blank to use 'lukasz'): admin
(01:07:40 PM) lukasz: E-mail address:
(01:07:40 PM) lukasz: Password: admin
(01:07:43 PM) lukasz: Password (again): admin
(01:07:55 PM) lukasz: Email address is not too important at that stage
(01:08:28 PM) lukasz: later you can configure Django to automatically receive crash reports on that address, but that's something more advanced
(01:09:15 PM) lukasz: Next bit is to create application, something where you put your code. By design you should separate different site modules into their own applications, that way it's easier to maintain it later and also if you create something which can be usable outside of your project you can share it with others without necessary putting all of your project out there. It's pretty popular in Django community, so it's always good idea to check
(01:09:15 PM) lukasz: somebody already haven't created something useful. That way you can save yourself reinventing the wheel.
(01:09:55 PM) lukasz: For this there's startapp command
(01:09:56 PM) lukasz: $ ./ startapp app
(01:10:13 PM) lukasz: In this simple case we're calling our application just 'app'
(01:10:39 PM) lukasz: This creates an 'app' directory in your project. Inside of it there are files created for you by Django.
(01:10:51 PM) lukasz: - is where your data model definitions go,
(01:11:01 PM) lukasz: - place to hold your views code.
(01:11:42 PM) lukasz: Maybe some short terms definition here. Django is sort of Model/View/Controller framework (not really according to its creators). Basically it separates all your code into three separate layers and in principle only code from layer above should get access to lower one.
(01:12:09 PM) lukasz: First layer are models, where data definitions lies. That's the thing you put into file. You define objects your application will manipulate.
(01:12:32 PM) lukasz: Above that are controllers which in Django are called views. This code responds to requests from users, manipulates the data and sends it to be rendered to the last layer, which is:
(01:12:44 PM) lukasz: view in standard world, but here those role is taken by templates.
(01:13:16 PM) lukasz: Next bit is to add this new application to list of installed apps in, that way Django knows from which parts your application is assembled.
(01:13:38 PM) lukasz: In file find variable named INSTALLED_APPS
(01:13:58 PM) lukasz: Add to the list: ''
(01:14:09 PM) lukasz: It should look like that:
(01:14:12 PM) lukasz:    INSTALLED_APPS = (
(01:14:12 PM) lukasz:      'django.contrib.auth',
(01:14:12 PM) lukasz:      'django.contrib.contenttypes',
(01:14:12 PM) lukasz:      'django.contrib.sessions',
(01:14:15 PM) lukasz:      'django.contrib.sites',
(01:14:18 PM) lukasz:      '',
(01:14:21 PM) lukasz:    )
(01:14:48 PM) lukasz: You can see that there are already things here, mostly things giving your project already built functionality
(01:15:31 PM) lukasz: Names are pretty descriptive so you shouldn't have problem with figuring out what each bit does
(01:15:58 PM) lukasz: Now we start making actual application. First thing is to create model which will hold user updates. Open file app/
(01:16:38 PM) lukasz: You define models in Django by defining classes with special attributes. That can be  translated by Django into table definitions and create appropriate structures in database.
(01:16:54 PM) lukasz: For now add following lines to the end of the file:
(01:17:10 PM) lukasz: (btw, bigger chunks of code are on pastebin)
(01:17:32 PM) lukasz: Now some explanations. You can see that you define model attributes by using data types defined in django.db.models module. Full list of types and options they can take is documented here:
(01:18:09 PM) lukasz: ForeignKey bit links our model with User model supplied by Django
(01:18:28 PM) lukasz: that way we can have multiple users having their updated on our site
(01:19:08 PM) lukasz: Another bit of magic is auto_now_add setting of the DateTimeFiled, makes that whenever we create new instance of this model this field will be set to current date and time. That way we don't have to worry about that. There's also auto_now option which sets such field to now whenever instance is modified.
(01:19:27 PM) lukasz: class Meta bit is place for settings for whole model. In this case we are saying that whenever we'll get list of updates we want them to be ordered by create_at field in ascending order (by default order is descending, and '-' means reversing that order).
(01:19:45 PM) lukasz: Now we have to synchronise data definition in with what is in database. For that we'll use already known command: syncdb
(01:19:50 PM) lukasz: $ ./ syncdb
(01:19:57 PM) lukasz: You should get following output:
(01:20:02 PM) lukasz: Creating table app_update
(01:20:02 PM) lukasz: Installing index for app.Update model
(01:20:17 PM) lukasz: Great thing about Python is it's interactive shell. You can easily use it with Django.
(01:20:26 PM) lukasz: You start it by
(01:20:27 PM) lukasz: $ ./ shell
(01:20:48 PM) lukasz: This runs interactive shell configured to work with your project. From here we can play with our models and create some updates.
(01:21:05 PM) lukasz: >>> from django.contrib.auth.models import User
(01:21:12 PM) lukasz: >>> admin = User.objects.get(username='admin')
(01:21:17 PM) lukasz: Here 'admin' is whatever you've chosen when asked for admin username.
(01:21:36 PM) lukasz: First thing is to get hold to our admin user, because every update belongs to someone. You can see that we used 'objects' attribute of model class.
(01:21:49 PM) lukasz: >>> from import Update
(01:21:55 PM) lukasz: >>> update = Update(owner=admin, status="This is first status update")
(01:22:16 PM) lukasz: At that point we have instance of the Update model, but it's not saved in the database
(01:22:50 PM) lukasz: you can see that by checking attribute
(01:22:57 PM) lukasz: Currently it's None
(01:23:08 PM) lukasz: >>>
(01:23:36 PM) lukasz: Now, when you saved it in database it has id
(01:23:45 PM) lukasz: >>>
(01:23:46 PM) lukasz: 1
(01:23:59 PM) lukasz: That's only one of many ways to create instances of the models, this one is the easiest one.
(01:24:07 PM) lukasz: You can check that update.created_at was set properly to current date:
(01:24:11 PM) lukasz: >>> update.created_at
(01:24:16 PM) lukasz: datetime.datetime(2009, 9, 2, 12, 23, 58, 659426)
(01:24:44 PM) lukasz: You can also see that you get back nice, Python datetime object instead of having to process whatever database returned for that field.
(01:24:55 PM) lukasz: When we have some data in the database there's time to somehow display it to the user.
(01:25:11 PM) lukasz: First bit for a view to work is to tell Django for which url such view should respond to. For that we have to modify file.
(01:25:18 PM) lukasz: Open it and add following line just under line with 'patterns' in it, so whole bit should look like that:
(01:25:29 PM) lukasz: urlpatterns = patterns('',
(01:25:30 PM) lukasz:     (r'^$', ''),
(01:25:30 PM) lukasz: )
(01:25:45 PM) lukasz: First bit there is regular expression for which this view will respond, in our case this is empty string (^ means beginning of the string and $ means end, so there's nothing in it), second bit is name of the function which will be called.
(01:25:55 PM) lukasz: Now go to app/ file. Here all code responsible for responding to users' requests will live.
(01:26:04 PM) lukasz: First bit is to import required bit from Django:
(01:26:09 PM) lukasz: from django.http import HttpResponse
(01:26:15 PM) lukasz: Now we can define our (very simple) view function:
(01:26:25 PM) lukasz: def home(request):
(01:26:25 PM) lukasz:     return HttpResponse("Hello from Django")
(01:26:50 PM) lukasz: As you can see every view function has at least one argument, which is request object, which contains lots of useful information about request, but for our simple example we'll not use it for now.
(01:26:56 PM) lukasz: After that we can start our app and check if everything is correct, to do that run:
(01:27:01 PM) lukasz: $ ./ runserver
(01:27:36 PM) lukasz: If everything went ok you should see following output
(01:27:38 PM) lukasz: Validating models...
(01:27:38 PM) lukasz: 0 errors found
(01:27:38 PM) lukasz:  
(01:27:41 PM) lukasz: Django version 1.0.2 final, using settings 'twitbuntu.settings'
(01:27:45 PM) lukasz: Development server is running at
(01:27:48 PM) lukasz: Quit the server with CONTROL-C.
(01:28:50 PM) lukasz: As you can see Django first checks if model definitions are correct and then starts our application. You can access it by going to in your browser of choice. What you should see is "Hello from Django" text.
(01:29:09 PM) lukasz: It would be nice to be able to log in to our own application, fortunately Django already has required pieces inside and only thing left for us is to hook them up.
(01:29:20 PM) lukasz: Everything else is already set up when we first used syncdb command.
(01:29:31 PM) lukasz: Add following two lines to the list of urls:
(01:29:38 PM) lukasz: (r'^accounts/login/$', 'django.contrib.auth.views.login'),
(01:29:38 PM) lukasz: (r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
(01:30:37 PM) lukasz: Next bit is to create template directory and enter it's location in file:
(01:30:46 PM) lukasz: $ mkdir templates
(01:30:53 PM) lukasz: In file find TEMPLATE_DIRS setting:
(01:31:07 PM) lukasz: import os
(01:31:08 PM) lukasz: TEMPLATE_DIRS = (
(01:31:08 PM) lukasz:     os.path.join(os.path.dirname(__file__), 'templates'),
(01:31:08 PM) lukasz: )
(01:31:18 PM) lukasz: This will ensure that Django can always find the template directory even if current working directory is not the one containing application (for example when run from Apache web server).
(01:31:42 PM) lukasz: Next is to create registration dir in templates directory and put there login.html file with following content:
(01:32:19 PM) lukasz: Last bit is to set up LOGIN_REDIRECT_URL in to '/':
(01:32:19 PM) lukasz: LOGIN_REDIRECT_URL = '/'
(01:32:31 PM) lukasz: That way after login user will be redirected to '/' url instead of default '/accounts/profile' which we don't have.
(01:32:49 PM) lukasz: Now getting to should present you the login form and you should be able to log in to application.
(01:33:07 PM) lukasz: Now when we can login it's time to use that information in our views.
(01:33:38 PM) lukasz: Django provides very convenient way of accessing logged in user by adding 'user' attribute to request object. It's either model instance representing logged in user or instance of AnonymousUser class which have same interface as model. Easiest way to distinguish those two is by using .is_authenticated() method on it.
(01:33:51 PM) lukasz: Modify our home view function so it looks like that:
(01:34:37 PM) lukasz: That way logged in users will be greeted and anonymous users will be sent to login form. You should see "Hello username" at
(01:34:55 PM) lukasz: Using that we can restrict access to our application. But it would be very repetitive having to enter same if statement in every function you want to protect, so there is more convenient of doing the same thing.
(01:35:07 PM) lukasz: Add following line to the top of the file:
(01:35:07 PM) lukasz: from django.contrib.auth.decorators import login_required
(01:35:24 PM) lukasz: This decorator does exactly what we have done manually but it's less code which doesn't hides what this view is doing, now we can shorten it to:
(01:35:43 PM) lukasz: Test view in your browser, nothing should have changed.
(01:36:39 PM) lukasz: Now when we have reliable way of getting to the user instance we can return all user's updates.
(01:37:00 PM) lukasz: When designing update model we have used ForeignKey type, this creates connection between two models. Later when we've created updates we used user instance as value of this attribute. That's one way of accessing this data (every update has owner attribute). Due to usage of ForeignKey pointing to User model every instance of it got also update_set attribute which contains every update which is assigned to this user.
(01:37:31 PM) lukasz: Clean way of getting all user updates is:
(01:37:31 PM) lukasz: >>> admin.update_set.all()
(01:37:31 PM) lukasz: [<Update: Update object>]
(01:37:40 PM) lukasz: But we can also get to the same information from Update model:
(01:37:40 PM) lukasz: >>> Update.objects.filter(owner=admin)
(01:38:01 PM) lukasz: (btw, those are only examples, you don't have to type them)
(01:38:13 PM) lukasz: Both of those will return the same data, only that first way is cleaner IMHO.
(01:38:32 PM) lukasz: That's just simple example of a way to get data from the database. You have far greater power over that aspect of your application but as time is short for us I don't get much deeper into that aspect.
(01:38:44 PM) lukasz: Now when we know how to get to necessary data we can send it to the browser by modifying home function:
(01:39:07 PM) lukasz: Here we set the content type of the response to text/plain so we can see angle brackets in the output, without that, by default, browser would hide it.
(01:39:22 PM) lukasz: Now, when we have data we can work on dressing it up a little bit. For that we'll use templates.
(01:39:31 PM) lukasz: Templates in Django have it's own syntax, it's really simple as it was designed to be used by designers, people not used to programming languages.
(01:39:39 PM) lukasz: We already have templates configured due to requirements of auth system, so it will be very easy to get started.
(01:39:51 PM) lukasz: First we need some template we can use. Create file template/home.html and put following content in it:
(01:40:16 PM) lukasz: Every tag in Django templates language is contained between {% %} elements and also ending of every opening thing is done by adding end(thing) to the end (like endfor in that case).
(01:40:29 PM) lukasz: To output content of the variable we're using {{ }} syntax. Also we can use something called filters by using | to pass value through the named filter. We're using that to format date to nice text description of time passed.
(01:40:52 PM) lukasz: That's template, now let's write view code to use it..
(01:41:18 PM) lukasz: There's very convenient function when using templates in views: render_to_response
(01:41:29 PM) lukasz: add following line to the top of the file
(01:41:30 PM) lukasz: from django.shortcuts import render_to_response
(01:41:45 PM) lukasz: This function takes two arguments: name of the template to render (usually it's file name) and dictionary of arguments to pass to template. Having this in mind our home view looks like that:
(01:42:22 PM) lukasz: Now running $ ./ runserver you can see that page in the browser has proper title
(01:42:47 PM) lukasz: It would be really nice to be able to add status updates from the web page. For that we need a form. There are couple ways of doing that in Django, but we'll show a way which is most useful for forms which are used to create/modify instances of the models.
(01:42:55 PM) lukasz: By convention form definitions goes to file in your app directory. Put following bits in there:
(01:43:04 PM) lukasz: This is very simple form which has only one field in it.
(01:43:19 PM) lukasz: Now in we need to instantiate this form and pass it to the template. After modifications this file should look like this:
(01:43:26 PM) lukasz: Last bit is to display this form in template. Add this bit just after <body> tag:
(01:43:37 PM) lukasz: <form action="" method="post">
(01:43:37 PM) lukasz:   <table>
(01:43:37 PM) lukasz:     {{ form }}
(01:43:37 PM) lukasz:     <tr><td colspan="2"><input type="submit" value="Update"/></td></tr>
(01:43:40 PM) lukasz:   </table>
(01:43:43 PM) lukasz: </form>
(01:44:00 PM) lukasz: Now when we have form properly displayed it would be useful to actually create updates based on the data entered by the user. That requires little bit of work inside our home view. Fortunately this is pretty straightforward to do:
(01:44:15 PM) lukasz: First thing is to check weather we're processing POST or GET request, if POST that means that user pressed 'Update' button on our form and we can start processing submitted data.
(01:44:30 PM) lukasz: All POST data is conveniently gathered by Django in a dictionary in request.POST. For this case it's not really critical to know what exactly is send, UpdateForm will handle that. Bit with instance= is to automatically set update owner, without that form would not be valid and nothing would be saved in the database.
(01:44:43 PM) lukasz: Checking if form is valid is very simple, just invoke .is_valid() method on it. If True is returned then we're saving the form to the database, which returns Update instance. It's not really needed anywhere but I wanted to show you that you can do something with it.
(01:44:56 PM) lukasz: Last bit is to create empty form, so that Status field will be clear, ready for next update.
(01:45:06 PM) lukasz: If you try to send update without any content you'll see that there's an error message displayed 'This field is required'. All of that is automatically handled by forms machinery.
(01:45:26 PM) lukasz: It's nice to be able to see our own status updates but currently it's only viewable by logged user.
(01:45:36 PM) lukasz: To implement this feature we'll start by adding new entry in Add following entry there:
(01:45:42 PM) lukasz: (r'^(?P<username>\w+)$', ''),
(01:45:55 PM) lukasz: This bit (?P<username>) is python extension to regular expression, it names a bit matched string. Using this name will enable us to write pretty convenient view function in
(01:46:18 PM) lukasz: First we'll import very convenient shortcut function: get_object_or_404 which gets you model instance if it exists in database or returns 404 Page not found page if such object doesn't exist.
(01:47:19 PM) lukasz: Then add user function as shown here:
(01:47:43 PM) lukasz: Last bit is to create 'user.html' template which will display this data properly.
(01:48:02 PM) lukasz: Quickly doing this yields something like that:
(01:48:19 PM) lukasz: Now you can go to and see your updates.
(01:48:50 PM) lukasz: (substitute username with the one you've chosen)
(01:49:08 PM) lukasz: It's all nice with templates but as you have noticed there are common things in both of our templates. We now have two of them but imagine project with tenths of templates, making change to some common thing would be really painful in such situation.
(01:49:26 PM) lukasz: Fortunately Django is designed to help in this area too. Feature we're talking about now is templates inheritance. Idea is that you can have base template which defines holes to be filled in by the more specific templates.
(01:49:39 PM) lukasz: Those holes are named blocks in Django. You define them like that:
(01:49:46 PM) lukasz: {% block some_block %}
(01:49:46 PM) lukasz:   Block content
(01:49:46 PM) lukasz: {% endblock %}
(01:50:05 PM) lukasz: When in template which inherits form such base template you provide data to such block it will replace anything defined before, but if you omit that block default data would be rendered to the end user.
(01:50:16 PM) lukasz: We'll start by defining our base template in templates/base.html file:
(01:50:22 PM) lukasz: This has some styling introduced, so our app will not look so ugly (not that improvement is dramatic ;D).
(01:51:01 PM) lukasz: Next bit is to update home.html and user.html templates to user this base template.
(01:51:01 PM) lukasz: Most important bit is extends tag which tells Django which template is base for the current one, for brevity I'll present only user.html template and you should be able to modify home.html by yourself:
(01:51:23 PM) lukasz: As a last bit I left Django admin interface, web application which enables you to manage data in database without need to use python shell or database access tools. It's distinct feature of Django which really speeds up web application development.
(01:51:36 PM) lukasz: First bit is to add admin as an installed app in your file:
(01:51:44 PM) lukasz: INSTALLED_APPS = (
(01:51:45 PM) lukasz:     ...
(01:51:45 PM) lukasz:     'django.contrib.admin',
(01:51:45 PM) lukasz: )
(01:52:04 PM) lukasz: In uncomment this on top of the file, so it looks like that:
(01:52:04 PM) lukasz: from django.contrib import admin
(01:52:04 PM) lukasz: admin.autodiscover()
(01:52:18 PM) lukasz: Last bit is to add admin urls to list of patterns:
(01:52:18 PM) lukasz: (r'^site-admin/(.*)',,
(01:52:27 PM) lukasz: In that case I changed default /admin/ url into /site-admin/ as my user is named admin and there would be collision (in that case order of patterns in file matter, as one higher have precedence).
(01:52:50 PM) lukasz: Last bit is to invoke syncdb which will create some tables necessary for admin to work properly.
(01:53:00 PM) lukasz: Now when you go to you should admin interface you your database.
(01:53:17 PM) lukasz: Now pretty much only thing you can do with admin is manage users, we don't see our own model there. For that we have to tell Django  how we want our data to be displayed.
(01:53:28 PM) lukasz: By convention bits connected with admin interface goes to file in your app directory. First enter it's content and then I'll describe what is there exactly.
(01:53:43 PM) lukasz: from django.contrib import admin
(01:53:43 PM) lukasz: from import Update
(01:53:43 PM) lukasz:  
(01:53:43 PM) lukasz:
(01:53:59 PM) lukasz: This is simplest possible form of telling Django about our model. After that you should be able to see it in Django admin interface.
(01:54:41 PM) lukasz: Now you're able to add new Updates, delete/change existing ones.
(01:54:54 PM) lukasz: But this is really simple and doesn't show us full potential of it. We'll change one bit to show how easy it is to customise it.
(01:55:26 PM) lukasz: For that we'll create UpdateAdmin class which will hold all customisations:
(01:55:48 PM) lukasz: Some short description of the fields
(01:55:53 PM) lukasz: list_display is list of model fields which should be displayed as columns on the list of objects
(01:56:03 PM) lukasz: search_fields is list of fields which will be checked when you try to search by using search box
(01:56:12 PM) lukasz: list_filter is list of fields which will create nifty right side filters for your objects which really speeds up looking through big sets of data.
(01:57:39 PM) lukasz: And that's all I prepared for today's session, you can find detailed information about every aspect of Django in documentation. Django documentation is great, you can almost always find everything you need there, also tutorial presented there is very good, goes into much more detail than we had time to do today.
(01:57:55 PM) achuni: (we're pretty much out of time.... questions?)
(01:59:38 PM) achuni: k people, thanks!
(01:59:48 PM) lukasz: Thank you everybody for your time, hope you enjoyed it
(02:00:30 PM) lukasz: and I hope you have some info you can start with

MeetingLogs/devweek0909/Django (last edited 2009-09-02 18:24:12 by ausimage)