django beginner workshop

Basics

django

  • MVT-Webframework
  • Python 2/3
  • Aktuelle Version 1.11 LTS
  • Pluggability
  • Don’t repeat yourself (DRY)
  • Introspection

Note

  • Benannt nach Django Reinhardt, Gitarrist
  • Kommt aus der Zeitungswelt

Wo wird django genutzt?

  • disqus
  • Pinterest
  • Instagram
  • Mozilla
  • NASA

Was beinhaltet django?

  • Webserver
  • Middleware
  • URL-Dispatcher
  • Views
  • Models: ORM-Mapper
  • Form-Validator
  • Admin-Interface
  • Templates

(Stark vereinfachter) Requestablauf

_images/django_structure.png

Note

Ein Request Object hat alle Informationen zu dem Request. Unter anderem Context-Variablen, Post/Get, URL, Languages.


Bei Fragen

Das Projekt

Ein Portfolio

  • Seiten

    • Startseite mit Projektübersicht
    • Projektdetailseite
    • Kontaktformular
  • Design nach Bedarf (http://getbootstrap.com/)


Datenmodell für unsere Seite

_images/project_orm.png

virtualenv (venv)

  • Kapseln der Python Pakete in einer unabhängigen “Installation”
  • Unterschiedliche Projekte können unterschiedliche Abhängigkeiten bekommen
  • Volle Unterstützung von pip
  • Keine root-Rechte für Installation von Paketen

venv einrichten

user@hostname:$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip...done.

user@hostname:$ source venv/bin/activate

(venv)user@hostname:$ deactivate

user@hostname:$

Note

Man kann auch virtualenv wrapper verwenden. Je nach Situation kann dieser Arbeit abnehmen.

http://virtualenvwrapper.readthedocs.org/en/latest/


django installieren

$ pip install django==1.11.*
$ pip install psycopg2 # Für Postgres
$ pip install Pillow   # Für Bilder
$ pip freeze

Warning

Für diesen Befehl und für alle weiteren Befehle muss man die venv aktiviert haben.


django installieren 2

$ python -c "import django; print(django.get_version())"
1.8.15

Das django admin tool

$ django-admin startproject portfolio

class:slidecenter

Was wurde erstellt?

Note

Besonders wichtig sind:

  • manage.py
  • settings.py
  • urly.py
  • wsgi.py

runserver

  • Einfacher debugging Server
  • NICHT(!) produktiv nutzen
  • autoreload (fehlerhaft)

runserver starten

$ python manage.py runserver 0.0.0.0:8000
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

October 04, 2017 - 16:32:20
Django version 1.11.6, using settings 'portfolio.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

It worked!

_images/django-startproject.png

Note

Im runserver sieht man logs aller requests Strg + C zum Beenden

View

Die erste “App” erstellen

  • Apps sind gekapselte Plugins für django
  • “Alles” muss und wird in einer App gekapselt
$ python manage.py startapp core

class:slidecenter

Was wurde erstellt?


App in den INSTALLED_APPS eintragen

in portfolio/settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'core',
)

Die erste View

In core/views.py:

from django.http import HttpResponse

def index(request):
    return HttpResponse("hello world!")

URL-Mapping


App urls.py

core/urls.py

from django.conf.urls import url
from core import views

urlpatterns = [
   url(r'^$', views.index, name='index')
]

Note

Viele regex Möglichkeiten, unter anderem mit Variablenzuweisung.


Importieren in der globalen Datei

portfolio/urls.py

from django.conf.urls import include, url
from django.contrib import admin
from core import urls as  core_urls

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^core/', include(core_urls))
]

Note

In der realen Welt wird man das vermutlich nicht so sauber trennen. Hier für den Workshop arbeiten wir aber so sauber wie möglich!


Recap: Wo stehen wir gerade?

_images/django_structure.png

Templates

Template Language

  • Absichtlich:
    • Einfacher Syntax
    • !KEINE LOGIK!
  • Wurde von vielen kopiert

Template Language

  • Tags: {% if %}, {% endif %}, {% load static %}
  • Filter: {{project.name|lower|striptags|truncatewords:2 }}
  • Vererbung: {% extends "base.html" %}, {% block content %}

Note


Template-HTML

core/templates/index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Portfolio</title>
    </head>
    <body>
        <h1>Hi!</h1>
        {{ message }}
    </body>
</html>

Template in der View verwenden

core/views.py

from django.template import RequestContext

def index(request):
    context_dict = {'message': "Ich komme aus dem context_dict"}
    return render(request, 'index.html', context_dic)

Statische Medieninhalte

portfolio/settings.py

STATIC_URL = '/static/'

Note

Statische Inhalte sind anders zu behandeln als Medieninhalte.


Im Template

core/templates/index.html

{% load staticfiles %}

 <img src="{% static 'example.jpg' %}" alt="Picture" />

Note

Das Bild dazu muss unter core/static/example.jpg liegen.


Assets einbinden

<link rel="stylesheet" href="{% static 'css/base.css' %}" />
<script src="{% static 'js/jquery.js' %}"></script>

Übung: “Über mich”-Seite

Database

Das erste Model

_images/project_orm.png

Datenbank

  • PostgreSQL
  • MySQL
  • SQLite
  • Oracle
  • (MSSQL)
  • (NOSQL)

SQLite

portfolio/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_PATH, 'portfolio.db'),
    }
}

Warning

Niemals SQLite produktiv nutzen


PostgreSQL

portfolio/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'db_name',
        'USER': 'db_user',
        'PASSWORD': 'db_user_password',
        'HOST': ''
    }
}

Models

  • Beschreibung von Daten in Python-Objekten
  • Wir bekommen “geschenkt”:
    • auto Validierungen
    • auto Forms
    • auto Admin

Models

core/models.py

class Kategorie(models.Model):
    name = models.CharField(max_length=128, unique=True, help_text="Der Name")
    def __unicode__(self):
        return self.name

class Project(models.Model):
    category = models.ForeignKey("Kategorie", related_name="projects", null=True)
    name = models.CharField(max_length=128, unique=True)
    def __unicode__(self):
        return self.name

Model Feld Typen

  • CharField / TextField
  • URLField
  • IntegerField
  • BooleanField
  • ImageField
  • DateField
  • ForeignKey / OneToOneField / ManyToManyField

Datenbank erstellen

$ python manage.py migrate

$ python manage.py makemigrations core

$ python manage.py migrate

Shell debugging

$ python manage.py shell

Shell debugging 2

>>> from core.models import Project

>>> print Project.objects.all()
[]

>>> p = Project(name="Test")

>>> print Project.objects.all()
[]

>>> p.save()

>>> print Project.objects.all()
[<Project: Test>]

>>> quit()

Note


class:slidecenter

django Admin

Note

Der Admin funktioniert über Introspection im Gegensatz zu zum Beispiel Ruby on Rails.


django Admin: aktivieren

INSTALLED_APPS = (
    'django.contrib.admin', #hier
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'core',
)

Note

Bei neuen django Installationen ist der Admin schon aktiviert.


class:slidecenter

django Admin Demo

Note

/admin


Models am admin registrieren

core/admin.py

from django.contrib import admin
from core.models import Project

admin.site.register(Project)

Populate-Skript

populate.py

import os

def populate():
    Project.objects.get_or_create(name="myProject")
    #....

    for p in Project.objects.all():
        print p

if __name__ == '__main__':
    print "Starting Population script..."
    os.environ.setdefault('DJANGO_SETTINGS_MODULE',
        'portfolio.settings')
    import django
    django.setup()
    from core.models import Project
    populate()

Der Adminuser im Template

core/templates/index.html

{% if user.is_authenticated %}
   nice to see you
{% else %}
   who are you
{% endif %}

class:slidecenter

Admin Showcase

Was so geht …

Model-Templates

Models im Template

  • Liste
    • View erweitern
    • Template erweitern
  • Detailseite
    • Neue View + URL-Mapping
    • Neues Template
    • Verlinkung von Liste

Models in Templates: View Liste

core/views.py

from core.models import Project
def index(request):
   #...
   project_list = Project.objects.all()
   context_dict = {'projects': project_list}
   #...

Template

core/templates/index.html

{% if projects %}
    <ul>
        {% for project in projects %}
           <li>{{ project.name }}</li>
        {% endfor %}
    </ul>
{% else %}
    <strong>Keine Projekte</strong>
{% endif %}

Model Detailseite: View Detail

core/views.py

from django.shortcuts import get_object_or_404
from core.models import Project
from django.template import RequestContext

def project(request, project_id):
    project = get_object_or_404(Project, id=project_id)
    context_dict = {'project': project}
    return render(request, 'project.html', context_dict)

Note

Siehe auch get_list_or_404


Model Detailseite: URL-Mapping

core/urls.py

url(r'^project/(?P<project_id>\d+)/$', views.project, name='project'),)

Note

Weitere Beispiele: * (?P<project_name>w+) * (?P<year>d{4}) * (?P<month>[a-z]{3})


Model Detailseite: Template

core/templates/project.html

<!DOCTYPE html>
<html>
    <head>
        <title>Project</title>
    </head>
    <body>
        <h1>{{ project.name }}</h1>
        <p>{{ project.copy }}</p>
    </body>
</html>

Model URLs Funktion am Model

core/models.py

def get_absolute_url(self):
    from django.core.urlresolvers import reverse
    return reverse('project', args=[str(self.id)])

Note

Um Objekte zu referenzieren, ist es sehr hilfreich, die Objekte um Funktionen zu erweitern wie get_absolute_url.


Verlinkung im Template

<a href="{{ project.get_absolute_url }}">{{ project.name }}</a>

Asset Management

Wir bauen einen einfachen Bildupload


Asset Management 1

Neues Attribut hinzufügen

core/models.py

image = models.ImageField(upload_to='project')

Asset Management 2

Settings Überprüfen

portfolio/settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Asset Management 3

Template Context Processor setzen für Media damit {{MEDIA_URL}} auch im Template zur Verfügung steht.

portfolio/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...
                'django.template.context_processors.media',
                ...
            ],
        },
    },
]

Asset Management 4

portfolio/urls.py

from django.conf.urls.static import static
from django.conf import settings

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Asset Management 5

Auf der Detailseite das Bild hinzufügen

core/templates/project.html

<img src="{{ MEDIA_URL }}{{ project.image }}">

Übung: Projekt-Kategorien

Kategorieliste mit Projekten darstellen

Forms

Form: Checkliste

  • forms.py erstellen
  • ModelForm für Model erstellen
  • Update-View erstellen (display, save, errors)
  • urls.py updaten
  • Template erstellen

forms.py und ModelsForm

core/forms.py

from django import forms
from core.models import Contact

class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact
        fields = ['name']

Note

Man KANN jedes Feld einzeln definieren. Das machen wir aber nicht.


Form: die View Dazu

core/views.py

from core.forms import ContactForm
def add_contact(request):
    context = RequestContext(request)
    if request.method == 'POST':
        form = ContactForm(request.POST, request.FILES)
        if form.is_valid():
            form.save(commit=True)
            return index(request)
        else:
            print form.errors
    else:
        form = ContactForm()
    return render(request, 'add_contact.html', {'form': form})

Form: URL-Mapping

core/urls.py

url(r'^contact/$', views.add_contact, name='add_contact'),

Form: Das Template

<form id="cform" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {% for hidden in form.hidden_fields %}
        {{ hidden }}
    {% endfor %}
    {% for field in form.visible_fields %}
        {{ field.errors }}
        {{ field.help_text }}
        {{ field }}
    {% endfor %}
    <input type="submit" name="submit" value="Contact Me!" />
</form>

Outro


Danke

Note

Good Bye!


Wie man mich erreichen kann