Skip to content
Snippets Groups Projects
introduction.html 16.3 KiB
Newer Older
Holger Dinkel's avatar
Holger Dinkel committed
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta name="generator" content="pandoc" />
  <title></title>
  <style type="text/css">code{white-space: pre;}</style>
  <link rel="stylesheet" href="github.css" type="text/css" />
</head>
<body>
<p>Tutor:</p>
<ul>
<li>Holger Dinkel</li>
</ul>
<p>Tutees:</p>
<ul>
<li>Petr Strnad</li>
<li>Charles Girardot</li>
<li>Joachim Weischenfeld</li>
<li>Guangyou Duan</li>
<li>Matthias Monfort</li>
<li>Serge Dmitrieff</li>
<li>Lucas Tafur</li>
<li>Jin Wang</li>
<li>Maia Segura Wang</li>
<li>Thomas Schwarzl</li>
<li>Sajoscha Sauer</li>
</ul>
<h1 id="django-introductory-tutorial">Django Introductory Tutorial</h1>
<p>These steps are pretty much taken and adapted from the official <a href="https://docs.djangoproject.com/en/1.7/intro/tutorial01/">Django Tutorial</a></p>
<p>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.</p>
<p>First, we start by creating a new project using the django-admin tool:</p>
<pre><code>django-admin.py startproject mysite</code></pre>
<p>This will create the folder 'mysite' along with some files in it.</p>
<pre><code>mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py</code></pre>
<p>These files are:</p>
<ul>
<li>The outer <strong>mysite</strong>/ root directory is just a container for your project. Its name doesn't matter to Django; you can rename it to anything you like.</li>
<li><strong>manage.py</strong>: A command-line utility that lets you interact with this Django project in various ways.</li>
<li>The inner <strong>mysite</strong>/ directory is the actual Python package for your project. Its name is the Python package name you'll need to use to import anything inside it (e.g. mysite.urls).</li>
<li><strong>mysite/<strong>init</strong>.py</strong>: An empty file that tells Python that this directory should be considered a Python package.</li>
<li><strong>mysite/settings.py</strong> : Settings/configuration for this Django project. Django settings will tell you all about how settings work.</li>
<li><strong>mysite/urls.py</strong>: The URL declarations for this Django project; a 'table of contents' of your Django-powered site.</li>
<li><strong>mysite/wsgi.py</strong>: An entry-point for WSGI-compatible web servers to serve your project.</li>
</ul>
<p>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):</p>
<pre><code>DATABASES = { 
    &#39;default&#39;: {
        &#39;ENGINE&#39;: &#39;django.db.backends.sqlite3&#39;,
        &#39;NAME&#39;: os.path.join(BASE_DIR, &#39;db.sqlite3&#39;),
    }   
}</code></pre>
<p>Now we initialize the database by running the migrate command:</p>
<pre><code>python manage.py migrate</code></pre>
<h2 id="running-django">Running Django</h2>
<p>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:</p>
<pre><code>python manage.py runserver</code></pre>
<h2 id="creating-apps">Creating apps</h2>
<p>Projects vs. apps</p>
<p>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.</p>
<p>To create your app, make sure you're in the same directory as manage.py and type this command:</p>
<pre><code>python manage.py startapp registrations</code></pre>
<p>That'll create a directory registrations, which is laid out like this:</p>
<pre><code>registrations/
    __init__.py
    admin.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py</code></pre>
<h3 id="philosophy">Philosophy</h3>
<p>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.</p>
<p>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.</p>
<h2 id="creating-models">Creating models</h2>
<p>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 &amp; lastname and a relational field to registrations.</p>
<p>These concepts are represented by simple Python classes. Edit the registrations/models.py file so it looks like this:</p>
<p>registrations/models.py</p>
<p>from django.db import models</p>
<pre><code>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)</code></pre>
<p>Note that Events has to be declared before Registrations as the latter references the former... (see the django documentation on the list of possible <a href="https://docs.djangoproject.com/en/1.7/ref/models/fields/#field-types">field types</a></p>
<p>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 &quot;INSTALLED_APPS&quot; in the settings.py</p>
<p>mysite/settings.py</p>
<pre><code>INSTALLED_APPS = (
    ...
    &#39;django.contrib.staticfiles&#39;,
    &#39;registrations&#39;,
)</code></pre>
<p>Now we can run the migration steps which in the background will create all the necessary complicated SQL statements to populate the database:</p>
<pre><code>python manage.py makemigrations registrations</code></pre>
<p>If you want, you can run a sanity check to see if django is ok with your configurations</p>
<pre><code>python manage.py check</code></pre>
<p>before you actually commit the migration:</p>
<pre><code>python manage.py migrate</code></pre>
<h2 id="playing-with-the-api">Playing with the API</h2>
<p>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:</p>
<pre><code>python manage.py shell</code></pre>
<p>Once you're in the shell, explore the database API:</p>
<pre><code>&gt;&gt;&gt; from registrations.models import Registration, Event

&gt;&gt;&gt; print Event.objects.all()
[]

&gt;&gt;&gt; e = Event(title=&quot;HUB15&quot;, description=&quot;User Experience workshop&quot;)
&gt;&gt;&gt; e.save()

&gt;&gt;&gt; e.id
1

&gt;&gt;&gt; print Event.objects.all()
[Event object &lt;...&gt;]

&gt;&gt;&gt; r = Registration(firstname=&#39;Holger&#39;, lastname=&#39;Dinkel&#39;, even=e)
&gt;&gt;&gt; r.save()</code></pre>
<h2 id="modifying-models">Modifying models</h2>
<p>Let's beautify the way that models are printed. For this we can add a function &quot;__str__&quot; to each model:</p>
<pre><code>class Event(models.Model):
    ...
    def __str__(self):
        return &quot;%s&quot; % self.title

class Registration(models.Model):
    ...
    def __str__(self):
        return &quot;%s %s&quot; % (self.firstname, self.lastname)</code></pre>
<p>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).</p>
<h2 id="using-the-admin-interface">Using the admin interface</h2>
<pre><code>python manage.py createsuperuser</code></pre>
<p>Now start the development server like so:</p>
<pre><code>python manage.py runserver</code></pre>
<p>and navigate your browser to http://127.0.0.1:8000/admin/ and provide your newly created credentials.</p>
<p>However our two models, Event &amp; Registration do not show up!? We first need to tell django to automatically include these in the admin interface:</p>
<p>registrations/admin.py</p>
<pre><code>from django.contrib import admin
from registrations.models import Event, Registration

admin.site.register(Event)</code></pre>
<p>Reload the admin page in your browser and both models should be there. Feel free to add/edit more entries for each...</p>
<h2 id="write-your-first-view">Write your first view</h2>
<p>Let's write the first view. Open the file registrations/views.py and put the following Python code in it:</p>
<p>registrations/views.py</p>
<pre><code>from django.http import HttpResponse

def index(request):
    return HttpResponse(&quot;Hello, world. You&#39;re at the registrations index.&quot;)</code></pre>
<p>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.</p>
<p>To create a URLconf in the registrations directory, create a file called urls.py. Your app directory should now look like:</p>
<pre><code>registrations/
    __init__.py
    admin.py
    models.py
    tests.py
    urls.py
    views.py</code></pre>
<p>In the registrations/urls.py file include the following code:</p>
<p>registrations/urls.py</p>
<pre><code>from django.conf.urls import patterns, url

from registrations import views

urlpatterns = patterns(&#39;&#39;,
    url(r&#39;^$&#39;, views.index, name=&#39;index&#39;),
)</code></pre>
<p>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</p>
<pre><code>from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns(&#39;&#39;,
    url(r&#39;^registrations/&#39;, include(&#39;registrations.urls&#39;)),
    url(r&#39;^admin/&#39;, include(admin.site.urls)),
)</code></pre>
<p>registrations/views.py</p>
<pre><code>from django.http import HttpResponse
from registrations.models import Event

def index(request):
    latest_event_list = Event.objects.order_by(&#39;-date&#39;)[:5]
    output = &#39;, &#39;.join([p.title for p in latest_event_list])
    return HttpResponse(output)</code></pre>
<p>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.</p>
<p>First, create a directory called templates in your registrations directory. Django will look for templates in there.</p>
<p>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.</p>
<p>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.</p>
<p>Put the following code in that template:</p>
<p>registrations/templates/registrations/index.html</p>
<pre><code>{% if latest_event_list %}
    &lt;ul&gt;
    {% for event in latest_event_list %}
        &lt;li&gt;&lt;a href=&quot;/registrations/{{ event.id }}/&quot;&gt;{{ event.title }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No registrations are available.&lt;/p&gt;
{% endif %}</code></pre>
<p>registrations/views.py</p>
<pre><code>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(&#39;-date&#39;)[:5]
    template = loader.get_template(&#39;registrations/index.html&#39;)
    context = RequestContext(request, {
        &#39;latest_event_list&#39;: latest_event_list,
    })
    return HttpResponse(template.render(context))</code></pre>
<p>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:</p>
<p>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.</p>
<p>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'.</p>
<p>registrations/views.py</p>
<pre><code>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, &#39;registrations/detail.html&#39;, {&#39;event&#39;: event})</code></pre>
<p>Next, we create a very simple 'detail' page like so:</p>
<p>registrations/templates/registrations/detail.html</p>
<pre><code>{{ event }}</code></pre>
<p>In order for this detail view to work, we need to update the urls.py</p>
<pre><code>url(r&#39;^(?P&lt;event_id&gt;\d+)/$&#39;, views.detail, name=&#39;detail&#39;),</code></pre>
<h2 id="writing-a-simple-form">Writing a simple form</h2>
<p>In order to be able to interact with the page, we'll create a simple form to enter data into the database:</p>
<p>registrations/detail.html</p>
<pre><code>&lt;h1&gt;{{ event.title }}&lt;/h1&gt;
{{ event.title }}: {{ event.description }} on {{ event.date}}

{% if error_message %}&lt;p&gt;&lt;strong&gt;{{ error_message }}&lt;/strong&gt;&lt;/p&gt;{% endif %}

&lt;p&gt;
Registered People:
&lt;ul&gt;
{% for registration in event.registration_set.all %}
&lt;li&gt;{{ registration }}&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;form action=&quot;{% url &#39;register&#39; event.id %}&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
Firstname:&lt;input type=&quot;text&quot; name=&quot;firstname&quot; /&gt;&lt;br/&gt;
Lastname:&lt;input type=&quot;text&quot; name=&quot;lastname&quot; /&gt;&lt;br/&gt;
&lt;input type=&quot;submit&quot; value=&quot;Register&quot; /&gt;
&lt;/form&gt;
&lt;/p&gt;</code></pre>
<p>Add another url to urls.py:</p>
<pre><code>url(r&#39;^(?P&lt;event_id&gt;\d+)/register$&#39;, views.register, name=&#39;register&#39;),</code></pre>
<p>and add another view to views.py:</p>
<pre><code>def register(request, event_id):
    event = get_object_or_404(Event, pk=event_id)
    firstname=request.POST[&#39;firstname&#39;]
    lastname=request.POST[&#39;lastname&#39;]
    try:
        selected_registration = event.registration_set.get(firstname=firstname, lastname=lastname)
        # Redisplay the question voting form.
        return render(request, &#39;registrations/detail.html&#39;, {
            &#39;event&#39;: event,
            &#39;error_message&#39;: &quot;Already registered&quot;,
        })
    except (KeyError, Registration.DoesNotExist):
        r = Registration(event = event, firstname = firstname, lastname= lastname)
        r.save()
    return render(request, &#39;registrations/detail.html&#39;, {&#39;event&#39;: event})</code></pre>
</body>
</html>