Werken met een database in Django

Werken met een database in Django

6 mei 2025
Erwin Matijsen
Geplaatst in Tutorial
Onderdeel van Werken met Django


In de vorige tutorial heb je geleerd hoe je dynamische gegevens in je templates kunt verwerken. Je hebt hiervoor tijdelijk gewerkt met een Car-dataclass en twee auto's aangemaakt, die je aan de context hebt toegevoegd.

Het resultaat was deze pagina:

Het eindresultaat van deze tutorial

In de praktijk werk je niet met dergelijke statische informatie, die je in je project uittypt. Je gegevens zullen veelal opgeslagen zijn in een database. In deze tutorial leer je hoe je gegevens vorm geeft zodat je met een database kunt werken. De basis hiervoor zijn models. Om de gegevens in je database te krijgen, leer je ook werken met de Django Admin. Tot slot leer je hoe je gegevens weer uit de database kunt krijgen en in je templates kunt tonen.

Het resultaat is precies hetzelfde als de afbeelding hierboven, maar dan op basis van dynamische gegevens die veel eenvoudiger te beheren en uit te breiden zijn.

Models

In de vorige tutorial heb je de volgende klasse gemaakt om auto's mee aan te maken:

models.py
from dataclasses import dataclass

@dataclass
class Car:
    name: str
    purchase_price: int
    purchase_year: int
    construction_year: int
    fuel_type: str
    km_at_purchase: int

Hiermee kun je één of meer auto's aanmaken en in je templates aanroepen. Dit werkt, maar is niet ideaal. In Django werk je vaak met een database, en specifiek met een relationele database. In een relationele database definieer je tabellen. In dit geval is een tabel Car. Je zou ook een tabel Owner kunnen maken, en een relatie maken tussen die twee tabellen. Elke auto heeft een eigenaar, elke eigenaar kan meerdere auto's hebben. Gegevens worden vervolgens opgeslagen in rijen. De tabel Car zou in mijn geval dus twee regels bevatten: één voor de Nissan Leaf, en één voor de Ford Focus.

Definiëren

De tabellen en relaties definiëren doe je in Django via modellen. Vervang in tracker/models.py de eerder aangemaakte dataclass door de volgende code:

models.py
from django.db import models


class Car(models.Model):

    # Fuel Type Choices
    fuel_type_choices = (
        ('benzine', 'Benzine'),
        ('diesel', 'Diesel'),
        ('elektrisch', 'Elektrisch'),
        ('hybride', 'Hybride'),
        ('lpg', 'LPG')
    )

    name = models.CharField(max_length=50)
    purchase_price = models.IntegerField()
    purchase_year = models.IntegerField()
    construction_year = models.IntegerField()
    fuel_type = models.CharField(max_length=20, choices=fuel_type_choices)
    km_at_purchase = models.IntegerField()

    def __str__(self):
        return self.name

Je maakt opnieuw een klasse Car aan, maar nu overerf je van models.Model. Vervolgens definieer je dezelfde velden als je in de eerdere dataclass had. Elk veld krijgt een bepaald type, wat correspondeert met de gegevenstypes in de database. Dus het veld name krijgt het type Charfield, en is dus een tekstveld. De meeste andere velden krijgen het type IntegerField, omdat het om (gehele) getallen gaat.

Ook fuel_type is een CharField, maar geef je ook de parameter choices mee. Deze keuzes heb je bovenin de klasse gedefinieerd, als een tuple van tuples. De eerste waarde van de tuples is de waarde zoals het in de database zal worden opgeslagen, de tweede waarde is waarde zoals in formulieren zichtbaar is (human readable). Door deze keuzes mee te geven, zullen ook alleen deze waardes toegestaan worden in de database.

Tot slot kun je nog een __str__-methode definiëren, waarmee je invloed uitoefent op wat je standaard ziet als je een instantie van de klasse toont.

Database voorbereiden en migreren

Je hebt nu Car gemodelleerd, maar daarmee is er nog niets aangemaakt in de database. Standaard werkt Django met een SQLite database. Voor ontwikkeling en kleine tot middelgrote projecten volstaat dit prima. Sqlite is een database dat, in tegenstelling tot de meeste databases, als zelfstandige applicatie kan draaien. Er is geen databaseserver en een databaseclient nodig. Je ziet het daarom veel terug in bijvoorbeeld mobiele applicaties. De kans is heel groot dat SQLite al op je systeem is geïnstalleerd. Zo niet, volg dan de instructies op de website van SQlite om dit alsnog te doen.

In conf/settings.py vind je de database-instellingen terug:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

Deze staan standaard zoals hierboven ingesteld bij het aanmaken van een nieuw Django-project. Je kunt ze natuurlijk wijzigen, door bijvoorbeeld een andere locatie of naam te kiezen. Ook kun je kiezen voor een andere database, waarbij PostgreSQL een populaire keuze is. Voor deze tutorial volstaat SQLite, en hoef je de instellingen niet aan te passen.

Het model moet als laatste stap nog omgezet worden naar tabellen in de database. Dit doe je door te migreren. Dit doe je met behulp van een management commando, ingebouwd in Django. In je project zie je als het goed is het bestandje manage.py staan. Dit bestand kun je aanroepen met verschillende commando's. Voor nu zijn er twee relevant:

shell
python manage.py makemigrations
python manage.py migrate

Met het eerste commando maak je migratiebestanden aan. Deze worden aangemaakt in je app(s), in een map migrations. Met het tweede commando voer je de migraties daadwerkelijk uit. Hiermee worden de tabellen in de database aangemaakt, op basis van jouw modellen. Er zal dus een tabel cars worden aangemaakt, met de verschillende velden met de juiste types. Omdat Car van models.Model overerft, krijg je er automatisch ook een primary key bij (een veld waarmee elke rij een uniek nummer krijgt).

Zou je nu iets wijzigen in je Car-model, bijvoorbeeld een veld toevoegen of verwijderen, dan moet je dezelfde commando's weer uitvoeren om je database up-to-date te brengen.

Django Admin

Je hebt nu een model en dit heeft zich via een migration vertaald in een database met de juiste structuur. Je kúnt nu direct in de database je rijen gaan toevoegen, maar er is een makkelijkere manier. Django heeft namelijjk nog een super power: de Django Admin. De Django Admin is een kant-en-klaar beheersysteem voor je data. Het is zeer aanpasbaar - waar we nu niet op ingaan. In de basis is het al heel erg bruikbaar.

Je moet wel de modellen die je via de Admin-omgeving wilt beheren expliciet toevoegen. Dit doe je via het admin.py-bestand in je app, in dit geval dus in tracker/admin.py. De meest eenvoudige implementatie is als volgt:

admin.py
from django.contrib import admin

from tracker.models import Car


@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
    pass

Hiermee voeg je de mogelijkheid toe om auto's te beheren via de Admin. Om in de Admin-omgeving te komen heb je nog wel een superuser nodig. Deze maak je aan met het commando createsuperuser:

shell
python manage.py createsuperuser

Volg de stappen op om een gebruiker aan te maken. Ga tot slot naar localhost:8000/admin/ en log daar in met de zojuist aangemaakte gegevens. Je ziet aan de linkerkant de modellen die je hebt toegevoegd. Je ziet standaard ook Groups en User, ingebouwde modellen van Django om gebruikers mee te beheren. Daaronder zie je Car staan. Klik je daarop, dan zie je de auto's die je al hebt toegevoegd. In dit geval heb ik één auto toegevoegd. Een extra auto toevoegen kan via het groene "+ Add" naast Cars of via "Add Car +" rechtsbovenin. Er verschijnt een eevoudig formulier waarmee je de gegevens kunt invullen.

De Django Admin

In de eerste tutorial leerde je over de URL's en zag je als het goed is in conf/urls.py "admin" al staan als URL. Deze wordt automatisch toegevoegd als je een Django-project start.

Klik je op een aangemaakte auto, dan kun je deze wijzigen of verwijderen.

Gegevens uit de database halen

Nu je een paar gegevens in je database hebt, wil je ze uiteraard ook weer op kunnen halen en in je templates tonen. Django heeft gelukkig een manier ingebouwd om eenvoudig met de database te kunnen communiceren. Een eenvoudig voorbeeld is:

cars = Cars.objects.all()

Hiermee haal je simpelweg alle auto's op uit de database en sla je ze op in de variabele cars. Een dergelijke constructie noem je een query. Je kunt ook filteren:

elektrisch = Car.objects.filter(fuel_type="elektrisch")

Hier zie je dat je filtert op het veld fuel_type. Uiteraard kun je ook op andere velden filteren, en hoef je niet alleen te filteren op 'is gelijk aan'. Wil je bijvoorbeeld alleen de auto's zien die een bouwjaar voor 2024 hebben:

cars = Car.objects.filter(construction_year__lt=2024)

Hier gebruik je de notatie __lt (twee underscores gevolgd door lt, wat staat voor lesser than). Er zijn nog veel meer mogelijkheden om te filteren. Voor nu is het voldoende om eenvoudige filters te maken. Wil je toch alvast meer weten, bekijk dan de documentatie over Field lookups.

Met .objects.filter() zoek je als het ware in alle objecten, en filter je eruit wat je nodig hebt. Maar je kunt ook situaties hebben dat je precies één resultaat nodig hebt, vaak op basis van een ID. Dit kan met .objects.get():

leaf = Car.objects.get(pk=1)

Net als bij .filter gebruik je bij .get een veld om op te filteren. In dit geval is dat veld pk, wat staat voor primary key. Dit is een veld dat Django zelf toevoegt aan elk model en voor elke rij (voor elk object) uniek is. Bestaat het object met het ID niet, dan zal er een DoesNotExist exception worden opgeworpen. Je kunt hierop controleren:

try:
    auto = Car.objects.get(pk=2)
except Car.DoesNotExist:
    auto = "Auto bestaat niet"
print(auto)

DoesNotExist is altijd beschikbaar op al je modellen. Omdat dit zo'n veelvoorkomende situatie is, is er ook een shortcut:

from django.shortcuts import get_object_or_404

auto = get_object_or_404(Car, pk=2)

Bestaat de auto niet, dan zal er een "Pagina niet gevonden" worden teruggegeven (Met HTML-code 404).

Toepassen

Er is nog veel meer mogelijk voor het ophalen van gegevens, maar met .filter en .get heb je de basis wel te pakken. Maar... waar plaats je deze code nu?

Juist, in de views! Daar waar je eerder zelf twee auto-objecten had aangemaakt in de overview-view, daar kun je nu deze queries.

views.py
from tracker.models import Car

def overview(request):

    cars = Car.objects.all()  # Haal alle auto's op

    context = {
        "cars": cars,  # Plaats ze in de context
        "name": "Erwin Matijsen",
    }

    return render(request, "overview.html", context=context)

Herlaad nu de Overview-pagina, en je zult alle auto's die je via de Django Admin hebt toegevoegd, hier terugzien.

Probeer zelf eens meer auto's toe te voegen en pas de query aan met een filter. Haal bijvoorbeeld alleen de auto's op van na een bepaald bouwjaar, of van een bepaald brandstoftype.

Conclusie

In deze tutorial heb je geleerd over het werken met Django's datamodel. Eerst heb je een model gemaakt, die je vervolgens met een migration om hebt gezet in een tabel in een SQLite database. Met behulp van de Django Admin kon je vervolgens eenvoudig objecten aanmaken, wijzigen en verwijderen in je database. De laatste stap was het ophalen van de gegevens met behulp van queries, en ze vervolgens weer te gebruiken in je templates.

Je ziet: alles wat nodig is om te werken met gegevens in een database zijn geïntegreerd in Django. Ze zeggen niet voor niets zelf: batteries included!

Over de auteur


Erwin Matijsen

Erwin Matijsen

Erwin is de oprichter van python-cursus.nl. In allerlei rollen heeft hij Python ingezet, van het eenvoudiger maken van zijn werk tot het opleveren van complete (web)applicaties. Met vrouw en kinderen woont hij in Havelte (Drenthe), midden in de prachtige natuur. Daar wandelt hij graag, zeker ook omdat de beste ingevingen tijdens een wandeling - weg van de computer - lijken te komen.


Contact

Vragen, opmerkingen?

Heb je vragen, opmerkingen, suggesties of tips naar aanleiding van deze blog? Neem dan contact met ons op, of laat het weten via Mastodon of LinkedIN.