Slide 01 β€” Setup

Django installieren & starten

Vom leeren Ordner zur laufenden Seite β€” in 5 Minuten.

1

Virtual Environment + Django installieren

Immer in einem venv arbeiten β€” hΓ€lt dein System sauber.

Terminal
# Venv erstellen und aktivieren
python -m venv venv
venv\Scripts\activate          # Windows
source venv/bin/activate       # Mac / Linux

# Django installieren
pip install django
2

Projekt erstellen & starten

startproject baut den Hauptordner. cd rein. runserver startet.

Terminal
django-admin startproject meinshop .   # Punkt = aktueller Ordner
python manage.py runserver

# β†’ Browser ΓΆffnen: http://127.0.0.1:8000
βœ…
Der grΓΌne Django-Startscreen bedeutet: alles lΓ€uft. Ctrl+C stoppt den Server.
3

App erstellen

Django trennt Logik in Apps. Eine App = ein Bereich (z.B. Shop, Blog, Login).

Terminal
python manage.py startapp shop

App in settings.py eintragen β€” sonst kennt Django sie nicht:

meinshop/settings.py
settings.py
INSTALLED_APPS = [
    # Django Standard-Apps ...
    'django.contrib.admin',
    'django.contrib.auth',
    # Deine App:
    'shop',    # ← hier eintragen
]
4

Views & URLs β€” so funktioniert Django

πŸ’‘
View = Python-Funktion die eine Seite zurΓΌckgibt.
URL = Adresse die sagt, welche View aufgerufen wird.
shop/views.py
views.py
from django.http import HttpResponse

def startseite(request):
    return HttpResponse(
        "Hallo Shop!"
    )
shop/urls.py ← neu erstellen
shop/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path(
      '',
      views.startseite,
      name='home'
    ),
]

Jetzt noch die Haupt-URLs verbinden:

meinshop/urls.py
meinshop/urls.py
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('shop.urls')),  # ← Shop-URLs einbinden
]
πŸ—‚οΈ
Regel: Jede App hat ihre eigene urls.py. Die Haupt-urls.py im Projektordner verbindet alle Apps mit include().
Projektstruktur nach Schritt 3
πŸ“ meinshop/ ← Root
πŸ“ meinshop/ ← Einstellungen
settings.py ← Konfiguration
urls.py ← Haupt-URLs
πŸ“ shop/ ← Deine App
views.py ← Logik
urls.py ← Routen der App
manage.py
1 / 7
Slide 02 β€” Templates

HTML einbinden

Echte HTML-Seiten statt HttpResponse-Text.

1

Templates-Ordner anlegen

Django sucht HTML-Dateien in einem templates/ Ordner innerhalb der App.

πŸ“ shop/
πŸ“ templates/ ← hier rein
πŸ“ shop/ ← Namensraum-Trick
index.html
views.py
πŸ’‘
Der doppelte shop/shop/-Ordner ist Django-Konvention um Namenskonflikte zu vermeiden, wenn du mehrere Apps hast.
2

HTML-Datei erstellen

shop/templates/shop/index.html
index.html
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <title>Mein Shop</title>
</head>
<body>
  <h1>Willkommen im Shop! πŸ›’</h1>
  <p>Hier kommen bald Produkte rein.</p>
</body>
</html>
3

View auf HTML-Template umstellen

render() lΓ€dt die HTML-Datei und gibt sie zurΓΌck.

shop/views.py
views.py
from django.shortcuts import render

def startseite(request):
    return render(request, 'shop/index.html')
4

Daten aus Python ans HTML schicken

Mit dem Context-Dictionary kannst du Python-Variablen ins HTML geben.

views.py
views.py
def startseite(request):
    context = {
        'titel': 'Mein Shop',
        'anzahl': 42,
    }
    return render(
        request,
        'shop/index.html',
        context
    )
index.html – Nutzung
index.html
<h1>{{ titel }}</h1>

<p>
  {{ anzahl }} Produkte
</p>

{# Das ist ein Kommentar #}
{% if anzahl > 0 %}
  <p>Auf Lager βœ…</p>
{% endif %}
πŸ”‘
Django Template Sprache (kurz):
{{ variable }} β†’ Wert ausgeben
{% if %} {% endif %} β†’ Bedingung
{% for item in liste %} {% endfor %} β†’ Schleife
2 / 7
Slide 03 β€” Static Files

CSS einbinden

Styling mit Static Files β€” CSS, JS, Bilder alles lΓ€uft so.

1

Static-Ordner anlegen

πŸ“ shop/
πŸ“ static/
πŸ“ shop/ ← gleicher Namensraum-Trick
style.css
2

Erste CSS-Datei

shop/static/shop/style.css
style.css
body {
    margin: 0;
    font-family: Arial, sans-serif;
    background: #f5f5f5;
}

h1 {
    color: #333;
    text-align: center;
    padding: 2rem;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 1rem;
}
3

CSS im HTML einbinden

{% load static %} muss oben stehen β€” dann funktioniert {% static %}.

shop/templates/shop/index.html
index.html
{% load static %}  <!-- ← Erste Zeile! -->
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <link
    rel="stylesheet"
    href="{% static 'shop/style.css' %}"
  >
</head>
<body>
  <div class="container">
    <h1>Mein Shop</h1>
  </div>
</body>
</html>
βœ…
STATICFILES_DIRS in settings.py brauchst du nicht, solange du den static-Ordner direkt in der App hast β€” Django findet ihn automatisch.
4

Tipp: Bootstrap einbinden (schnellstes Styling)

Kein lokales CSS nΓΆtig β€” einfach CDN-Link einfΓΌgen und fertig.

im <head>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
>
🎨
Mit Bootstrap hast du sofort ein professionelles Grid, Buttons, Cards, Navbar usw. β€” ideal fΓΌr Shop-Prototypen. Empfohlen fΓΌr Einsteiger!
3 / 7
Slide 04 β€” Base Template

Navigationsbar & base.html

Einmal schreiben β€” ΓΌberall haben. Das Kern-Konzept von Django Templates.

🧠
Das Konzept: Du schreibst eine base.html mit der Navbar. Alle anderen Seiten erben davon mit {% extends %}. Navbar Γ€ndern? Nur einmal, in base.html β€” fertig.
1

Ordnerstruktur fΓΌr base.html

base.html kommt in einen globalen templates-Ordner auf Root-Ebene β€” nicht in der App.

πŸ“ meinshop/ ← Root
πŸ“ templates/ ← global
base.html ← hier
πŸ“ shop/
πŸ“ templates/shop/
index.html
πŸ“ meinshop/
settings.py
2

settings.py β€” Django sagen wo Templates liegen

meinshop/settings.py
settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],  # ← diese Zeile!
        'APP_DIRS': True,
        # ... rest bleibt gleich
    },
]
3

base.html β€” mit vollstΓ€ndiger Navbar

Das ist die komplette, professionelle Vorlage. Einmal anlegen, nie wieder anfassen.

templates/base.html
base.html
{% load static %}
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{% block title %}Mein Shop{% endblock %}</title>

  <!-- Bootstrap CSS -->
  <link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
  >

  <!-- Eigenes CSS -->
  <link rel="stylesheet" href="{% static 'shop/style.css' %}">

  <!-- Extra CSS pro Seite mΓΆglich -->
  {% block extra_css %}{% endblock %}
</head>
<body>

  <!-- ═══ NAVBAR ═══ -->
  <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container">

      <!-- Logo / Brand -->
      <a class="navbar-brand fw-bold"
         href="{% url 'home' %}">πŸ›’ MeinShop</a>

      <!-- Hamburger fΓΌr Mobile -->
      <button class="navbar-toggler"
              type="button"
              data-bs-toggle="collapse"
              data-bs-target="#navMenu">
        <span class="navbar-toggler-icon"></span>
      </button>

      <!-- MenΓΌ-Links -->
      <div class="collapse navbar-collapse" id="navMenu">
        <ul class="navbar-nav me-auto">

          <li class="nav-item">
            <a class="nav-link"
               href="{% url 'home' %}">Home</a>
          </li>

          <li class="nav-item">
            <a class="nav-link"
               href="{% url 'produkte' %}">Produkte</a>
          </li>

          <li class="nav-item">
            <a class="nav-link"
               href="{% url 'kontakt' %}">Kontakt</a>
          </li>

        </ul>

        <!-- Login-Bereich rechts in der Navbar -->
        <ul class="navbar-nav">
          {% if user.is_authenticated %}
            <li class="nav-item">
              <span class="nav-link">
                Hallo, {{ user.username }}
              </span>
            </li>
            <li class="nav-item">
              <a class="nav-link"
                 href="{% url 'logout' %}">Logout</a>
            </li>
          {% else %}
            <li class="nav-item">
              <a class="nav-link btn btn-outline-light btn-sm px-3"
                 href="{% url 'login' %}">Login</a>
            </li>
          {% endif %}
        </ul>

      </div>
    </div>
  </nav>

  <!-- ═══ SEITENINHALT ═══ -->
  <main class="container py-4">
    {% block content %}{% endblock %}
  </main>

  <!-- ═══ FOOTER ═══ -->
  <footer class="bg-dark text-light text-center py-3 mt-5">
    <p class="mb-0">Β© 2025 MeinShop</p>
  </footer>

  <!-- Bootstrap JS (fΓΌr Hamburger-MenΓΌ) -->
  <script
    src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
  ></script>

  {% block extra_js %}{% endblock %}
</body>
</html>
4

index.html β€” erbt jetzt von base.html

shop/templates/shop/index.html
index.html
{% extends 'base.html' %}
{% load static %}

{% block title %}Home – Mein Shop{% endblock %}

{% block content %}
  <h1>Willkommen im Shop! πŸ›’</h1>
  <p>Hier kommen bald Produkte.</p>
{% endblock %}
🎯
Das war's. {% extends 'base.html' %} oben β†’ Navbar + Footer kommen automatisch.
Nur {% block content %} musst du befΓΌllen. Alle anderen Seiten genauso.
4 / 7
Slide 05 β€” Routing

Neue Seiten hinzufΓΌgen

Immer das gleiche Schema: View β†’ URL β†’ Template. Einmal verstehen, immer wissen.

πŸ”
Das Schema fΓΌr jede neue Seite: (1) View-Funktion in views.py β†’ (2) URL in shop/urls.py β†’ (3) HTML-Template erstellen β†’ (4) Link in base.html Navbar ergΓ€nzen
Beispiel: 3 Seiten auf einmal
1

Alle Views in views.py

shop/views.py
views.py
from django.shortcuts import render

def startseite(request):
    return render(request, 'shop/index.html')

def produkte(request):
    return render(request, 'shop/produkte.html')

def kontakt(request):
    return render(request, 'shop/kontakt.html')

def ueber_uns(request):
    return render(request, 'shop/ueber_uns.html')
2

Alle URLs in shop/urls.py

shop/urls.py
shop/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('',           views.startseite, name='home'),
    path('produkte/',  views.produkte,   name='produkte'),
    path('kontakt/',   views.kontakt,    name='kontakt'),
    path('ueber-uns/', views.ueber_uns,  name='ueber_uns'),
]
⚑
name= ist wichtig! Damit machst du Links mit {% url 'name' %} im HTML β€” und wenn du die URL-Adresse Γ€nderst, musst du nur hier Γ€ndern, nicht in 20 HTML-Dateien.
3

Template fΓΌr jede neue Seite (Boilerplate)

Jede neue Seite startet mit diesen 6 Zeilen. Nur der Inhalt im block Γ€ndert sich.

shop/templates/shop/produkte.html
produkte.html
{% extends 'base.html' %}
{% load static %}

{% block title %}Produkte{% endblock %}

{% block content %}
  <h1>Unsere Produkte</h1>
  <p>Produkte kommen im nΓ€chsten Slide πŸ˜„</p>
{% endblock %}
4

Interne Links β€” immer mit {% url %}

❌ Falsch β€” nie hardcoden

Falsch
<a href="/produkte/">
  Produkte
</a>

βœ… Richtig β€” mit url tag

Richtig
<a href="{% url 'produkte' %}">
  Produkte
</a>
5 / 7
Slide 06 β€” Media

Bilder, Produkt-Cards & Videos

Flexible Grâßen, Shop-Cards, und Videos β€” ohne das lΓ€stige 25/50/100%-Problem.

Statische Bilder einbinden
1

Bild in static-Ordner legen

πŸ“ shop/static/shop/
style.css
πŸ“ images/
produkt1.jpg ← hier rein
HTML – Bild einbinden
{% load static %}

<!-- Beliebige Breite: einfach width= setzen -->
<img
  src="{% static 'shop/images/produkt1.jpg' %}"
  alt="Produkt 1"
  style="width: 400px; height: 300px; object-fit: cover;"
>

<!-- ODER: Prozent relativ zum Elternelement -->
<img
  src="{% static 'shop/images/produkt1.jpg' %}"
  alt="Produkt 1"
  style="width: 100%; height: 250px; object-fit: cover;"
>
πŸ”‘
object-fit: cover ist der Trick! Das Bild fΓΌllt den Rahmen komplett aus ohne verzerrt zu werden β€” egal welche Grâße du setzt. Kein lΓ€stiges Strecken mehr.
2

Produkt-Cards β€” Shop-Grid mit Bootstrap

Komplett flexibel: du kontrollierst HΓΆhe im CSS, Spalten im Grid.

shop/templates/shop/produkte.html
produkte.html – Shop Grid
{% extends 'base.html' %}
{% load static %}
{% block content %}

<h2 class="mb-4">Produkte</h2>

<!-- 3 Spalten auf Desktop, 1 auf Mobile -->
<div class="row row-cols-1 row-cols-md-3 g-4">

  <!-- Wenn Produkte aus der Datenbank kommen: -->
  {% for produkt in produkte_liste %}
  <div class="col">
    <div class="card h-100 shadow-sm">

      <!-- BILD: exakt 280px hoch, egal wie groß das Original -->
      <img
        src="{% static 'shop/images/produkt.jpg' %}"
        class="card-img-top"
        alt="{{ produkt.name }}"
        style="height: 280px; object-fit: cover;"
      >

      <div class="card-body d-flex flex-column">
        <h5 class="card-title">{{ produkt.name }}</h5>
        <p class="card-text text-muted">{{ produkt.beschreibung }}</p>
        <p class="fw-bold fs-5 mt-auto">{{ produkt.preis }} €</p>
        <a href="#" class="btn btn-dark mt-2">In den Warenkorb</a>
      </div>
    </div>
  </div>
  {% empty %}
    <p>Noch keine Produkte. FΓΌge welche in views.py hinzu.</p>
  {% endfor %}

</div>
{% endblock %}

Und in views.py gibst du eine Test-Liste mit:

views.py
def produkte(request):
    produkte_liste = [
        {'name': 'T-Shirt', 'preis': 19.99, 'beschreibung': 'Super Shirt'},
        {'name': 'Hoodie',  'preis': 49.99, 'beschreibung': 'Warm und weich'},
        {'name': 'MΓΌtze',   'preis': 12.99, 'beschreibung': 'Cozy af'},
    ]
    return render(request, 'shop/produkte.html', {
        'produkte_liste': produkte_liste
    })
3

Hero-Banner β€” Bild das die ganze Breite fΓΌllt

HTML
<!-- Volle Breite, 500px hoch, Text darΓΌber -->
<div style="
  position: relative;
  width: 100%;
  height: 500px;
  overflow: hidden;
">
  <img
    src="{% static 'shop/images/hero.jpg' %}"
    alt="Hero"
    style="
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: center;
    "
  >
  <!-- Text ΓΌber dem Bild -->
  <div style="
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    color: white;
    text-align: center;
  ">
    <h1>Willkommen im Shop</h1>
    <a href="{% url 'produkte' %}"
       class="btn btn-light btn-lg">Jetzt shoppen</a>
  </div>
</div>
4

Videos einbinden

Option A: Lokales Video (static)

Lokal
{% load static %}

<video
  style="width: 100%; height: 400px; object-fit: cover;"
  autoplay
  muted
  loop
>
  <source
    src="{% static 'shop/videos/clip.mp4' %}"
    type="video/mp4"
  >
</video>

Option B: YouTube einbetten

YouTube
<!-- Responsive 16:9 -->
<div style="
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
">
  <iframe
    src="https://www.youtube.com/embed/VIDEO_ID"
    style="
      position: absolute;
      top: 0; left: 0;
      width: 100%; height: 100%;
    "
    allowfullscreen
  ></iframe>
</div>
🎬
Der 56.25% padding-bottom Trick bei YouTube = perfektes 16:9-SeitenverhÀltnis auf jeder Bildschirmgrâße, ganz ohne JavaScript.
6 / 7
Slide 07 β€” Auth

Login-System einbauen

Django bringt ein komplettes Auth-System mit. Du musst fast nichts selbst bauen.

🎁
Django Auth ist bereits installiert. Registrierung, Login, Logout, Passwort Γ€ndern β€” alles fertig. Du musst nur noch die Seiten verbinden und gestalten.
1

Datenbank initialisieren

Nur einmal nΓΆtig β€” legt alle Django-Tabellen (inkl. User) an.

Terminal
python manage.py migrate

# Admin-User erstellen (optional, aber nΓΌtzlich)
python manage.py createsuperuser
# β†’ Benutzername, E-Mail, Passwort eingeben
# β†’ http://127.0.0.1:8000/admin/ ΓΆffnen
2

Auth-URLs einbinden

Django stellt Login/Logout-URLs fertig bereit β€” einfach in der Haupt-urls.py includen.

meinshop/urls.py
meinshop/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('shop.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
    # ↑ Das gibt dir automatisch:
    # /accounts/login/
    # /accounts/logout/
    # /accounts/password_change/
    # /accounts/password_reset/
]
3

settings.py β€” Weiterleitungen festlegen

meinshop/settings.py β€” ans Ende anhΓ€ngen
settings.py
# Nach Login β†’ Home
LOGIN_REDIRECT_URL = '/'

# Nach Logout β†’ Login-Seite
LOGOUT_REDIRECT_URL = '/accounts/login/'

# Login-Seite URL
LOGIN_URL = '/accounts/login/'
4

Login-Template erstellen

Django sucht das Login-Template unter registration/login.html.

πŸ“ templates/
base.html
πŸ“ registration/ ← diesen Ordner anlegen
login.html ← Django sucht genau diesen Namen
password_reset_form.html ← optional
templates/registration/login.html
login.html
{% extends 'base.html' %}

{% block title %}Login{% endblock %}

{% block content %}
<div class="row justify-content-center">
  <div class="col-md-4">

    <div class="card shadow-sm mt-5">
      <div class="card-body p-4">

        <h3 class="mb-4 text-center">πŸ” Login</h3>

        <form method="post">
          {% csrf_token %}  <!-- IMMER! Sicherheit -->

          <!-- Benutzername -->
          <div class="mb-3">
            <label class="form-label">Benutzername</label>
            <input
              type="text"
              name="username"
              class="form-control"
              required
            >
          </div>

          <!-- Passwort -->
          <div class="mb-3">
            <label class="form-label">Passwort</label>
            <input
              type="password"
              name="password"
              class="form-control"
              required
            >
          </div>

          <!-- Fehler anzeigen -->
          {% if form.errors %}
            <div class="alert alert-danger">
              Falscher Benutzername oder Passwort.
            </div>
          {% endif %}

          <button type="submit" class="btn btn-dark w-100">
            Einloggen
          </button>
        </form>

        <hr>
        <p class="text-center mb-0">
          Noch kein Account?
          <a href="{% url 'register' %}">Registrieren</a>
        </p>

      </div>
    </div>
  </div>
</div>
{% endblock %}
5

Registrierung selbst bauen (Django hat keine fertige)

shop/views.py β€” ergΓ€nzen
views.py
from django.contrib.auth.forms import \
    UserCreationForm
from django.shortcuts import \
    render, redirect

def register(request):
  if request.method == 'POST':
    form = UserCreationForm(
        request.POST
    )
    if form.is_valid():
      form.save()
      return redirect('login')
  else:
    form = UserCreationForm()
  return render(
    request,
    'registration/register.html',
    {'form': form}
  )
templates/registration/register.html
register.html
{% extends 'base.html' %}
{% block content %}

<div class="row justify-content-center">
<div class="col-md-5 mt-5">
<div class="card shadow-sm">
<div class="card-body p-4">
  <h3>Registrieren</h3>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button
      type="submit"
      class="btn btn-dark w-100"
    >Account erstellen</button>
  </form>
</div></div></div></div>

{% endblock %}
shop/urls.py β€” Register-URL ergΓ€nzen
shop/urls.py
urlpatterns = [
    path('',           views.startseite, name='home'),
    path('produkte/',  views.produkte,   name='produkte'),
    path('kontakt/',   views.kontakt,    name='kontakt'),
    path('register/',  views.register,   name='register'),  # ← NEU
]
6

Seiten schΓΌtzen β€” nur fΓΌr eingeloggte User

views.py
from django.contrib.auth.decorators import login_required

@login_required  # ← eine Zeile drΓΌber = Seite geschΓΌtzt
def warenkorb(request):
    return render(request, 'shop/warenkorb.html')

# Wer nicht eingeloggt ist β†’ wird automatisch zu /accounts/login/ weitergeleitet
🏁 Du kannst jetzt einen Shop-Prototyp bauen
βœ… Was du hast
βœ” Django-Projekt lΓ€uft
βœ” Navbar auf allen Seiten
βœ” Produkt-Cards mit Bildern
βœ” Flexible Bildgrâßen
βœ” Videos eingebettet
βœ” Login & Registrierung
βœ” Seiten schΓΌtzen
πŸš€ NΓ€chste Schritte
β†’ Models + Datenbank
β†’ Warenkorb-Logik
β†’ Stripe-Zahlung
β†’ Deployment (Railway/Render)
β†’ Admin-OberflΓ€che nutzen
β†’ Media Files (User-Uploads)
πŸŽ“
Jetzt einfach loslegen. Den besten Code schreibt man nicht durch Lesen β€” sondern durch Bauen, Fehler machen und debuggen. Du schaffst das. πŸ’ͺ
7 / 7