Django

Op deze pagina:

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>

 

Verwante artikelen