Tutor:
Tutees:
These steps are pretty much taken and adapted from the official Django Tutorial
In this tutorial we want to create a simple webpage which allows users to register to certain events by providing some information about themselves and choosing a an event from a list of created entries. We also want to use the builtin django-admin system to easily create/edit/remove entries in our database.
First, we start by creating a new project using the django-admin tool:
django-admin.py startproject mysite
This will create the folder 'mysite' along with some files in it.
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
These files are:
Let's have a look at the settings.py file first and in particular inspect the database settings (we will be using sqlite for now):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
Now we initialize the database by running the migrate command:
python manage.py migrate
Finally let's see if we can run the development server to verify everything worked as expected. Change into the outer mysite directory, if you haven't already, and run the following commands:
python manage.py runserver
Projects vs. apps
What's the difference between a project and an app? An app is a Web application that does something - e.g., a Weblog system, a database of public records or a simple registrations app. A project is a collection of configuration and apps for a particular Web site. A project can contain multiple apps. An app can be in multiple projects.
To create your app, make sure you're in the same directory as manage.py and type this command:
python manage.py startapp registrations
That'll create a directory registrations, which is laid out like this:
registrations/
__init__.py
admin.py
migrations/
__init__.py
models.py
tests.py
views.py
A model is the single, definitive source of data about your data. It contains the essential fields and behaviors of the data you're storing. Django follows the DRY Principle. The goal is to define your data model in one place and automatically derive things from it.
This includes the migrations - unlike in Ruby On Rails, for example, migrations are entirely derived from your models file, and are essentially just a history that Django can roll through to update your database schema to match your current models.
In our simple registrations app, we'll create two models: Events and Registrations. An Event has a date, a title and a description. A Registration has three fields: First & lastname and a relational field to registrations.
These concepts are represented by simple Python classes. Edit the registrations/models.py file so it looks like this:
registrations/models.py
from django.db import models
class Event(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128)
description = models.TextField(blank=True)
date = models.DateTimeField()
class Registration(models.Model):
id = models.AutoField(primary_key=True)
firstname = models.CharField(max_length=128)
lastname = models.CharField(max_length=128)
event = models.ForeignKey(Event)
Note that Events has to be declared before Registrations as the latter references the former... (see the django documentation on the list of possible field types
Each model will be represented in the database by an individual table. Django will create these for us when we invoke the migration commands, but first we need to tell django that it should actually search for and use these new models. We do this by adding our app 'registrations' to the list of "INSTALLED_APPS" in the settings.py
mysite/settings.py
INSTALLED_APPS = (
...
'django.contrib.staticfiles',
'registrations',
)
Now we can run the migration steps which in the background will create all the necessary complicated SQL statements to populate the database:
python manage.py makemigrations registrations
If you want, you can run a sanity check to see if django is ok with your configurations
python manage.py check
before you actually commit the migration:
python manage.py migrate
Now, with the database in place, let's drop into the interactive Python shell and play around with the free API Django gives you. To invoke the Python shell, use this command:
python manage.py shell
Once you're in the shell, explore the database API:
>>> from registrations.models import Registration, Event
>>> print Event.objects.all()
[]
>>> e = Event(title="HUB15", description="User Experience workshop")
>>> e.save()
>>> e.id
1
>>> print Event.objects.all()
[Event object <...>]
>>> r = Registration(firstname='Holger', lastname='Dinkel', even=e)
>>> r.save()
Let's beautify the way that models are printed. For this we can add a function "__str__" to each model:
class Event(models.Model):
...
def __str__(self):
return "%s" % self.title
class Registration(models.Model):
...
def __str__(self):
return "%s %s" % (self.firstname, self.lastname)
So next time we print an object we will get something more readable. Note how with this easy way you just effectively added a method to a database structure (both represented in Django).
python manage.py createsuperuser
Now start the development server like so:
python manage.py runserver
and navigate your browser to http://127.0.0.1:8000/admin/ and provide your newly created credentials.
However our two models, Event & Registration do not show up!? We first need to tell django to automatically include these in the admin interface:
registrations/admin.py
from django.contrib import admin
from registrations.models import Event, Registration
admin.site.register(Event)
Reload the admin page in your browser and both models should be there. Feel free to add/edit more entries for each...
Let's write the first view. Open the file registrations/views.py and put the following Python code in it:
registrations/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the registrations index.")
This is the simplest view possible in Django. To call the view, we need to map it to a URL - and for this we need a URLconf.
To create a URLconf in the registrations directory, create a file called urls.py. Your app directory should now look like:
registrations/
__init__.py
admin.py
models.py
tests.py
urls.py
views.py
In the registrations/urls.py file include the following code:
registrations/urls.py
from django.conf.urls import patterns, url
from registrations import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
)
The next step is to point the root URLconf at the registrations.urls module. In mysite/urls.py insert an include(), leaving you with: mysite/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns('',
url(r'^registrations/', include('registrations.urls')),
url(r'^admin/', include(admin.site.urls)),
)
registrations/views.py
from django.http import HttpResponse
from registrations.models import Event
def index(request):
latest_event_list = Event.objects.order_by('-date')[:5]
output = ', '.join([p.title for p in latest_event_list])
return HttpResponse(output)
There's a problem here, though: the page's design is hard-coded in the view. If you want to change the way the page looks, you'll have to edit this Python code. So let's use Django's template system to separate the design from Python by creating a template that the view can use.
First, create a directory called templates in your registrations directory. Django will look for templates in there.
Django's TEMPLATE_LOADERS setting contains a list of callables that know how to import templates from various sources. One of the defaults is django.template.loaders.app_directories.Loader which looks for a 'templates' subdirectory in each of the INSTALLED_APPS - this is how Django knows to find the registrations templates even though we didn't modify TEMPLATE_DIRS.
Within the templates directory you have just created, create another directory called registrations, and within that create a file called index.html. In other words, your template should be at registrations/templates/registrations/index.html. Because of how the app_directories template loader works as described above, you can refer to this template within Django simply as registrations/index.html.
Put the following code in that template:
registrations/templates/registrations/index.html
{% if latest_event_list %}
<ul>
{% for event in latest_event_list %}
<li><a href="/registrations/{{ event.id }}/">{{ event.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No registrations are available.</p>
{% endif %}
registrations/views.py
from django.http import HttpResponse
from django.template import RequestContext, loader
from registrations.models import Event
def index(request):
latest_event_list = Event.objects.order_by('-date')[:5]
template = loader.get_template('registrations/index.html')
context = RequestContext(request, {
'latest_event_list': latest_event_list,
})
return HttpResponse(template.render(context))
It's a very common idiom to load a template, fill a context and return an HttpResponse object with the result of the rendered template. Django provides a shortcut:
The 'render()' function takes the request object as its first argument, a template name as its second argument and a dictionary as its optional third argument. It returns an HttpResponse object of the given template rendered with the given context.
the 'get_object_or_404' function is handy to retrieve an object from the database or automatically return a 404 error message saying 'object not found'.
registrations/views.py
from django.http import HttpResponse
from django.template import RequestContext, loader
from django.shortcuts import get_object_or_404, render
from registrations.models import Event
(...)
def detail(request, event_id):
event = get_object_or_404(Event, pk=event_id)
# Equivalent to:
# try:
# event = Event.objects.get(pk=event_id)
# except Event.DoesNotExist:
# raise Http404
return render(request, 'registrations/detail.html', {'event': event})
Next, we create a very simple 'detail' page like so:
registrations/templates/registrations/detail.html
{{ event }}
In order for this detail view to work, we need to update the urls.py
url(r'^(?P<event_id>\d+)/$', views.detail, name='detail'),
In order to be able to interact with the page, we'll create a simple form to enter data into the database:
registrations/detail.html
<h1>{{ event.title }}</h1>
{{ event.title }}: {{ event.description }} on {{ event.date}}
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<p>
Registered People:
<ul>
{% for registration in event.registration_set.all %}
<li>{{ registration }}</li>
{% endfor %}
</ul>
</p>
<p>
<form action="{% url 'register' event.id %}" method="post">
{% csrf_token %}
Firstname:<input type="text" name="firstname" /><br/>
Lastname:<input type="text" name="lastname" /><br/>
<input type="submit" value="Register" />
</form>
</p>
Add another url to urls.py:
url(r'^(?P<event_id>\d+)/register$', views.register, name='register'),
and add another view to views.py:
def register(request, event_id):
event = get_object_or_404(Event, pk=event_id)
firstname=request.POST['firstname']
lastname=request.POST['lastname']
try:
selected_registration = event.registration_set.get(firstname=firstname, lastname=lastname)
# Redisplay the question voting form.
return render(request, 'registrations/detail.html', {
'event': event,
'error_message': "Already registered",
})
except (KeyError, Registration.DoesNotExist):
r = Registration(event = event, firstname = firstname, lastname= lastname)
r.save()
return render(request, 'registrations/detail.html', {'event': event})