In dit hoofdstuk ga je kleuren toevoegen aan elk product. Product is een voorbeeld van een N:M-relatie.
5.1Uitleg: Tabellen met N:M-relatie maken¶
5.2Opdracht: Voeg artikel-kleuren toe aan de database¶
In deze opdracht ga je kleuren (Engels: colors) toevoegen aan elk artikel in de database. Elk artikel heeft één of meer kleuren. Een kleur wordt gebruikt in één of meer artikelen.
De kleur wordt niet automatisch zichtbaar in je webshop, dat doen we in de volgende opdracht.
Tips
- Zie de tips bij de opdracht soorten-tabel toevoegen uit het vorige hoofdstuk.
Tips
- Zie de tips bij de opdracht soorten toevoegen aan de tabel uit het vorige hoofdstuk.
Tips
- Voeg de verwijzende sleutels en de constraints toe aan de tabel
product_color
. - De volgorde waarin je tabellen in je SQL-bestand zet doet ertoe. De tabel
product_color
moet je na de tabellenproducts
encolors
maken, anders dan kun je er niet naar verwijzen in je constraints in de tabelproduct_color
. - Als je niet meer weet hoe je een constraint toevoegt, bekijk dan Uitleg SQL voorbeeld N:M-relatie
Tips
- Maak een
INSERT INTO
-opdracht die deproduct_color
-tabel vult. - Gebruik in de
INSERT INTO
-opdracht de veldenproduct_id
encolor_id
. - Geef in je
INSERT INTO
-opdracht de nummers van de producten en kleuren die bij elkaar passen op. Elke conmbinatie is een nieuwe rij in de tabel.
5.3Uitleg: JSON¶
5.4Uitleg: Query met N:M-relatie maken¶
Query met SELECT FROM JOIN JOIN voor N:M-relatie¶
Bij een N:M-relatie combineer je drie tabellen:
- de koppeltabel (bijv.
product_colors
) - de eerste tabel (bijv.
products
) - de tweede tabel (bijv.
colors
)
Het maakt in sql niet uit met welke tabel je begint. Wij beginnen met de koppeltabel en leggen van daaruit maak je verbinding met de andere twee tabellen.
Voorbeeld: producten met hun kleuren
SELECT colors.name, products.name
FROM product_colors
JOIN colors ON product_colors.color_id = colors.id
JOIN products ON product_colors.product_id = products.id;
Wat gebeurt er in deze query?
SELECT
kiest de kolommen die je wilt zien:- de naam van de kleur
- de naam van het product
FROM product_colors
betekent dat je begint in de koppeltabelJOIN colors
koppelt elkecolor_id
aan een rij incolors
JOIN products
koppelt elkeproduct_id
aan een rij inproducts
Je krijgt dan een lijst van combinaties van kleur en product.
5.5Opdracht: Maak kleuren per artikel zichtbaar in de webshop¶
Je hebt in de vorige opdracht kleur-informatie aan alle artikelen toegevoegd in de database. Maar deze informatie is nog niet te zien op de webshop. Dat komt omdat de API de kleuren niet opvraagt uit de database. Omdat er meerdere kleuren per product kunnen zijn, moeten we de kleuren in een aparte query opvragen. In deze opdracht ga je de API aanpassen, zodat de informatie over kleuren wordt toegevoegd aan de informatie over artikelen die wordt opgestuurd naar de client. De client is zo gemaakt dat hij deze extra informatie automatisch toont.
Maak kleuren-query en voeg die toe aan artikel-informatie in JSON-formaat¶
De onderstaande code vraagt voor elk artikel de kleuren op uit de database en voegt deze toe aan de artikelinformatie (product_rows
). De API stuurt product_rows
in JSON-formaat naar de client.
Maak de query in de code af door op de plekken met <maak af>
de juiste stukje query in te vullen. De query eindigt met een vraagteken ?
. Op die plek wordt steeds het product_id
ingevuld van het product waarvan we de kleuren opvragen.
# Add values for n:m property (e.g., colors) to products
for product in product_rows:
# Fetch colors for the product
color_query = """
SELECT <maak af>
FROM <maak af>
JOIN <maak af>
WHERE product_id = ?
"""
# Execute the query to fetch colors for the current product
color_rows = db_connection.execute(color_query, (product["id"],)).fetchall()
# Add fetched colors to product
colors = []
for row in color_rows:
colors.append(row["name"])
product["kleur"] = colors
Kopieer de code op de juiste plek in de API en test of hij het doet.
Performance
We gebruiken voor elk product een aparte query om de kleuren uit de database op te vragen. Dit is niet heel efficient, maar wel eenvoudig. In een webshop met veel meer artikelen zouden programmeurs ervoor kiezen om de kleuren van alle producten tegelijk in één query op te vragen.
SQL-injection
In onze code wordt de execute
functie gebruikt om de query uit te voeren. De execute
-functie vult ook de parameters in, zoals product_id
. We hadden die parameter ook gewoon met +
aan de query kunnen toevoegen,maar dat doen we bewust niet.
Tijdens het invullen controleert de execute
-functie of er geen rare dingen in het product_id
staan. In deze code komt de waarde van product_id
uit de database waar alleen ontwikkelaars bij kunnen. Bij het hoofdstuk over filters zul je zien dat parameters ook door de client (de computer van bezoekers) opgestuurd kunnen worden. Deze parameters zouden bezoekers kunnen veranderen voordat ze ze opsturen.
Door de waarde van de parameter slim te kiezen kunnen gebruikers informatie uit de database halen die niet voor hen bedoeld is. Denk bijvoorbeeld aan een parameter product_id
met de waarde 1; SELECT * FROM passwords;
. De database denkt dan dat hij twee opdrachten moet uitvoeren waarvan de laatste de wachtwoorden geeft. Deze truuk heet SQL-injectie.
In de praktijk is het meestal niet zo gemakkelijk om een systeem te hacken met SQL-injectie, onder andere omdat wachtwoorden niet zomaar in een database worden gezet. Maar als je Googlet op SQL-injection, dan zie je dat het een veelvoorkomend probleem is. In onze database kan dit niet gebeuren, omdat we alle parameters door de execute
-functie laten controlere.
Tips
- De aanpassing moet je doen in de API, in het bestand
/app/main.py
bij de functie voor het endpoint/api/products/
. - Let op de commentaarregel om te zien naar welke plek je de code exact moet kopieren.
- Voeg aan de query een
SELECT
toe met één veld, namelijk de naam van de kleur. - Voeg aan de query een
JOIN
toe tussen de tabellenproduct_color
encolors
. - Controleer wat de API opstuurt naar de client. Zet in je browser achter de hostname van je webshop
/api/products/
en laadt die webpagina. Het antwoord is hetzelfde antwoord als wat de client van de API zou krijgen. Je ziet de artikelinformatie in JSON-formaat.