Django
Op deze pagina:
- Voorbereidingen
- venv gebruiken
- Django installeren
- Een Django project starten
- App starten
- urls.py in hoofdmap
- Run server
- app views.py
- app urls.py
- app toevoegen in de hoofdmap
- templates
- static files
- Variabelen meegeven aan templates
- Dynamische url
- tags gebruiken voor urls
- een basis template gebruiken en uitbreiden in andere templates
- includes gebruiken
- models bepalen de database
- Database bijwerken
- De admin module
- een geuploade afbeelding in een template plaatsen
- Django admin aanpassen
- Meerdere tabellen (models) gebruiken en koppelen
- Formulier
- HTML gebruiken in een invoerveld en dit weergeven als gerenderde html
- Paginatie
Voorbereidingen
Eerst pip installeren: sudo apt install python3-pip
Python versie opvragen: python3 --version
Opvragen welke venv pakketten beschikbaar zijn, die voor de huidige Python versie moet geïnstalleerd worden.
apt search venv
Dan venv installeren: sudo apt install python3.10-venv
Een directory maken voor de applicatie. Bijvoorbeeld vanuit je 'home' directory: mkdir -p Projecten/web
venv gebruiken
Je mag de virtuele omgeving zelf een naam geven, bijvoorbeeld: djenv
aanmaken: python3 -m venv djenv
activeren: source djenv/bin/activate
Als je klaar bent kun je de venv sluiten met: deactivate
Django installeren
Django installeren: pip install django
Django versie opvragen: django-admin --version
Een requirements.txt bestand aanmaken: pip freeze > requirements.txt
Een Django project starten
Een project starten in de huidige directory;
django-admin startproject website .
De dot (punt) in de bovenstaande opdracht zorgt ervoor dat de installatie in de huidige directory wordt gedaan en er geen nieuwe directory wordt aangemaakt met dezelfde naam.
De directory structuur wordt dan: Project/web/website. Het Django-project is 'website', er moet nog een django-app komen die 'blog' heet. Die moet ook in de directory 'web' komen.
App starten
django-admin startapp blog
urls.py in hoofdmap
urls
Run server
De server ten behoeve van de ontwikkeling, deze is niet geschikt voor de praktijk.
Poort 8000 gebruiken: poort 8000 is de defaultpoort, hoef je dus niet toe te voegen als je deze wilt gebruiken. Als je een andere poort wilt gebruiken, dan wel toevoegen.
python manage.py runserver 8000
Gebruik Ctrl-C om de server te stoppen
app views.py
Hier komen de request handlers
Voorbeeld:
from django.shortcuts import render
from django.http import HttpResponse
def hallo(request):
return HttpResponse('Hallo wereld!')
app urls.py
Deze moet jezelf toevoegen aan de app, in de hoofdmap staat ook al een urls.py
Voorbeeld:
from django.urls import path
from . import views
urlpatterns = [
# path('blog/', views.hallo) # domein.com/blog
path('', views.hallo) # komt uit op het domein.com/, dus zonder verdere toevoegingen.
]
De site is nu te vinden op http://127.0.0.1:8000/
app toevoegen in de hoofdmap
In settings.py toevoegen aan INSTALLED_APPS : blog
In urls.py eerst een import toevoegen: from django.urls import path, include
. De import path was er al, de import include komt er nu bij.
In urls.py toevoegen aan urlpatterns : path('', include('blog.urls'))
templates
In de 'blog' directory een directory toevoegen met de naam templates. Op de achtergrond voegt Django alle templates directory's bij elkaar, dus er bestaat een kans op 'name clashes'. Daarom in de templates directory nog een directory toevoegen met de naam blog. In deze directory een bestand toevoegen, zoals index.html.
In views.py moet de import van render worden toegevoegd als deze er nog niet staat:
from django.shortcuts import render
from django.http import HttpResponse
def hallo(request):
return HttpResponse('Hallo wereld!') # domein/
def index(request):
return render(request, 'blog/index.html') # domein/blog
static files
In de 'blog' directory een directory aanmaken met de naam static. Op de achtergrond voegt Django alles static directory's bij elkaar, dus er bestaat een kans op 'name clashes'. Daarom in deze directory weer een directory aanmaken met de app_naam als naam voor de directory.In deze app_naam directory dan weer directory's aanmaken voor scripts, images, styles etc.
Een static bestand toevoegen in een html template kan dan met:
{% load static %}
<link rel="stylesheet" href="{% static 'blog/styles/base.css' %}">
"{% load static %}" voeg je toe aan het begin van de html template.
Variabelen meegeven aan templates
In views.py kun je variabelen meegeven aan templates door een dictionary toe te voegen als derde argument van render:
from django.shortcuts import render
def index(request):
artikelen = [
{'titel': 'Eerste artikel'},
{'titel': 'Tweede artikel'}
]
return render(request, 'app_naam/index.html',{
'toon_artikelen': True,
'artikelen': artikelen
})
In de template kun je deze variabelen dan gebruiken via de dubbele gekrulde haken, je kunt ook logica gebuiken zoals if/else en for:
{% if toon_artikelen %}
{% for artikel in artikelen %}
<p>{{ artikel.titel }}</p>
{% endfor %}
{% else %}
<p>Geen artikelen gevonden!</p>
{% endif %}
Dynamische url
In urls.py (:slug maakt een slug verplicht):
urlpatterns = [
path('app_naam/', views.index), # domein.com/app_naam
path('app_naam/<slug:artikel_slug>', views.artikel_details), # domein.com/app_naam/<dynamic>
]
In views.py moet meetup_slug ook worden meegegeven als parameter:
def artikel_details(request, artikel_slug):
tags gebruiken voor urls
In urls.py van app_naam kun je een name toevoegen:
urlpatterns = [
path('app_naam/', views.index, name='alle_artikelen'), # domein.com/app_naam
path('app_naam/<slug:artikel_slug>', views.artikel_details, name='artikel_details'), # domein.com/app_naam/<dynamic>
]
In de html-template kun je dan de url-tag gebruiken, samen met de dynamic slug:
<a href={% url 'artikel_details' artikel.slug %} class="btn:>
een basis template gebruiken en uitbreiden in andere templates
In een basis template kun je blocks gebruiken die in specifieke templates dan worden ingevuld.
In de basis template:
<title>{% block title %}hier een defaultwaarde voor dit block{% endblock %}</title>
In de specifieke templates moet je dan aan het begin een extends plaatsen, deze template moet in de templates directory staan:
{% extends 'app_naam/base.html' %}
{% load static %}
...
{% block title %}hier een specifieke waarde voor dit block{% endblock %}
...
includes gebruiken
Je kunt stukken html invoegen uit de template directory met include:
{% include 'app_naam/item.html' %}
Je kunt ook specifieke parameters meegeven aan een include via with:
{% include 'app_naam/item.html' with title=artikel.title inhoud=artikel.inhoud %}
models bepalen de database
Een 'id' field wordt automatisch toegevoegd door Django.
Voorbeeld:
from django.db import models
class Artikel(models.Model):
titel = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
inhoud = models.TextField()
image = models.ImageField(upload_to='images')
Om 'images' te kunnen uploaden, moet je nog iets toevoegen in settings.py:
MEDIA_ROOT = BASE_DIR / 'uploads'
MEDIA_URL = '/files/'
Om met images te kunnen werken moet Pillow geinstalleerd zijn:
python -m pip install Pillow
Ook de 'hoofd' urls.py moet worden aangepast ten behoeve van door gebruikers geuploade afbeeldingen:
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Database bijwerken
Stop de server en gebruik makemigrations om database instructies te genereren en migrate om ze uit te voeren:
python manage.py makemigrations
python manage.py migrate
De admin module
Je hebt een log-in nodig, die maak je via createsuperuser:
python manage.py createsuperuser
Er wordt dan gevraagd om een naam, een e-mail-adres en een (veilig) wachtwoord (2x invoeren).Hierna de server herstarten om te kunnen inloggen als superuser op localhost:8000/admin.
Eigen models toevoegen aan Django administration via admin.py:
from django.contrib import admin
from .models import Blog
admin.site.register(Blog)
Database beschikbaar maken in views.py:
from .models import Blog
def index(request):
artikelen = Blog.objects.all()
return render(request, 'meetups/index.html', {
'meetups': meetups
})
def meetup_details(request, artikel_slug):
try:
selected_artikel = Blog.objects.get(slug=artikel_slug)
return render(request, 'blog/artikel_details.html', {
'artikel_gevonden' : True,
'artikel_titel' : selected_artikel.titel,
'artikel_description' : selected_artikel.description
})
except Exception as exc:
return render(request, 'blog/artikel_details.html', {
'artikel_gevonden' : False
})
Met meetup_found kun je dan niet-bestaande slugs afvangen.
een geuploade afbeelding in een template plaatsen
De afbeelding als parameter doorgeven aan een template:
with ... image=meetup_item.image.url
In de template kun je dan image gebruiken:
<img href="{{ image }}" alt="een geuploade afbeelding van ...">
Django admin aanpassen
De kolommen voor een model (databasetabel) aanpassen kan in admin.py:
class ArtikelAdmin(admin.ModelAdmin):
list_display = ('titel', 'slug')
list_filter = ('titel',)
prepopulated_fields = {'slug': ('titel',)}
admin.site.register(Artikel, ArtikelAdmin)
Meerdere tabellen (models) gebruiken en koppelen
In models.py Location toevoegen:
from django.db import models
class Location(models.Model):
name = models.CharField(max_length=200)
address = models.CharField(max_length=300)
def __str__(self):
return f'{self.name} ({self.address})'
class Meetup(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
description = models.TextField()
image = models.ImageField(upload_to='images')
location = models.ForeignKey(Location, on_delete=models.CASCADE)
def __str__(self):
return f'{self.title} - {self.slug}'
Om deze ook in Admin te zien, moet dit worden toegevoegd in admin.py:
from django.contrib import admin
from .models import Meetup, Location
class MeetupAdmin(admin.ModelAdmin):
list_display = ('title', 'slug')
list_filter = ('title',)
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Meetup, MeetupAdmin)
admin.site.register(Location)
Participanten toevoegen in models.py:
class Participant(models.Model):
email = models.EmailField(unique=True)
def __str__(self):
return self.email
class Meetup(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
description = models.TextField()
image = models.ImageField(upload_to='images')
location = models.ForeignKey(Location, on_delete=models.CASCADE)
participants = models.ManyToManyField(Participant, blank=True)
def __str__(self):
return f'{self.title} - {self.slug}'
Om deze ook in Admin te zien, moet dit worden toegevoegd in admin.py:
from django.contrib import admin
from .models import Meetup, Location, Participant
class MeetupAdmin(admin.ModelAdmin):
list_display = ('title', 'slug')
list_filter = ('title',)
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Meetup, MeetupAdmin)
admin.site.register(Location)
admin.site.register(Participant)
Datum veld toevoegen in models.py:
date = models.DateField()
Formulier
Voeg een forms.py toe in de directory 'app_naam'.
from django import forms
from .models import Participant
class RegistrationForm(forms.ModelForm):
class Meta:
model = Participant
fields = ['email']
In views.py moet ook iets worden aangepast voor het formulier:
from django.shortcuts import render
from .models import Meetup
from forms import RegistrationForm
def index(request):
meetups = Meetup.objects.all()
# meetups = Meetup.objects.all().order_by("-date") # voor sorteren op datum: aanhalingstekens gebruiken!
return render(request, 'meetups/index.html', {
'meetups': meetups
})
def meetup_details(request, meetup_slug):
try:
selected_meetup = Meetup.objects.get(slug=meetup_slug)
registration_form = RegistrationForm()
return render(request, 'meetup/meetup_details.html', {
'meetup_found' : True,
'meetup_title' : selected_meetup.title,
'meetup_description' : selected_meetup.description,
'form' : registration_form
})
except Exception as exc:
return render(request, 'meetup/meetup_details.html', {
'meetup_found' : False
})
In meetup-details.html:
<form action="{% url 'meetup-detail' meetup.slug %}" method="POST">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<div id="registration-actions">
<button>Register</button>
</div>
</form>
In views.py moet nu ook iets worden aangepast voor het afhandelen van het formulier:
from django.shortcuts import render
from .models import Meetup
from forms import RegistrationForm
def index(request):
meetups = Meetup.objects.all()
return render(request, 'meetups/index.html', {
'meetups': meetups
})
def meetup_details(request, meetup_slug):
try:
selected_meetup = Meetup.objects.get(slug=meetup_slug)
if request.method == 'GET' # formulier nog niet ingevuld
registration_form = RegistrationForm()
else: # POST, formulier was ingevuld en moet hier afgehandeld worden
registration_form = RegistrationForm(request.POST)
if registration_form.is_valid():
participant = registration_form.save()
selected_meetup.participants.add(participant)
return render(request, 'meetup/meetup_details.html', {
'meetup_found' : True,
'meetup_title' : selected_meetup.title,
'meetup_description' : selected_meetup.description,
'form' : registration_form
})
except Exception as exc:
return render(request, 'meetup/meetup_details.html', {
'meetup_found' : False
})
Een template toevoegen voor de succesvolle registratie:
registration-success.html
{% extends 'meetups/base.html' %}
{% load static %}
{% block title %}You registered successfully!{% endblock %}
{% block site_css %}
<link rel="stylesheet" href="{% static 'meetups/styles/new.css %}"
{% endblock %}
{% block main_heading %}Thanks for signing up!{% endblock %}
{% block body %}
<section id="confirmation">
<h2>You registered successfully!</h2>
<p>Thanks for signing up - see you at the meetup!</p>
<p>Got questions or can't make it? Please contact the organizer ahead of time!</p>
</section>
{% endblock %}
In views.py toevoegen:
def confirm_registration(request):
return render(request, 'meetups/registration-success.html')
In urlps.py toevoegen:
urlpatterns = [
path('app_naam/', views.index, name='all_meetups'), # domein.com/app_naam
path('meetups/success', views.confirm_registration, name='confirm-registration'), # deze moet eerst om te voorkomen dat hij bij onderstaande als waarde wordt gebruikt
path('app_naam/<slug:meetup_slug>', views.meetup_details, name='meetup_detail'), # domein.com/app_naam/<dynamic>
]
In views.py de redirect toevoegen:
from django.shortcuts import render, redirect
# en verderop;
else: # POST, formulier was ingevuld en moet hier afgehandeld worden
registration_form = RegistrationForm(request.POST)
if registration_form.is_valid():
participant = registration_form.save()
selected_meetup.participants.add(participant)
return redirect('confirm-registration')
HTML gebruiken in een invoerveld en dit weergeven als gerenderde html
{{ artikel.inhoud | safe }}
het bovenstaande zal ervoor zorgen dat de HTML in het veld inhoud wordt weergegeven als gerenderde HTML.
Paginatie
Voorbeeld vna hte gebruik van Paginator in een view functie ten behoeve van paginatie:
from django.core.paginator import Paginator
from django.shortcuts import render
from myapp.models import Contact
def listing(request):
contact_list = Contact.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page.
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
return render(request, "list.html", {"page_obj": page_obj})
In de HTML template kun je de paginatie bijvoorbeeld zo opnemen:
{% for contact in page_obj %}
{# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br>
...
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last »</a>
{% endif %}
</span>
</div>