Kattava opas sisäkkäisten objektien serialisointiin Django REST Frameworkissa (DRF) käyttäen serialisoijia, kattaen erilaiset suhdetyypit ja edistyneet tekniikat.
Python DRF Serializer -suhteet: Sisäkkäisten objektien serialisoinnin hallinta
Django REST Framework (DRF) tarjoaa tehokkaan ja joustavan järjestelmän web-APIen rakentamiseen. Keskeinen osa API-kehitystä on tietomallien välisten suhteiden käsittely, ja DRF-serialisoijat tarjoavat vankat mekanismit sisäkkäisten objektien serialisointiin ja deserialisointiin. Tämä opas tutkii erilaisia tapoja hallita suhteita DRF-serialisoijissa, tarjoten käytännön esimerkkejä ja parhaita käytäntöjä.
Serializer-suhteiden ymmärtäminen
Relaatiotietokannoissa suhteet määrittävät, miten eri taulut tai mallit on yhdistetty. DRF-serialisoijien on heijastettava näitä suhteita, kun tietokantaobjekteja muunnetaan JSON-muotoon tai muihin datamuotoihin API-käyttöä varten. Käymme läpi kolme pääasiallista suhdetyyppiä:
- ForeignKey (yksi-moneen): Yksi objekti liittyy useisiin muihin objekteihin. Esimerkiksi yksi kirjailija voi kirjoittaa monia kirjoja.
- ManyToManyField (monta-moneen): Useat objektit liittyvät useisiin muihin objekteihin. Esimerkiksi useat kirjailijat voivat tehdä yhteistyötä useiden kirjojen parissa.
- OneToOneField (yksi-yhteen): Yksi objekti liittyy yksilöllisesti toiseen objektiin. Esimerkiksi käyttäjäprofiili on usein linkitetty yksi-yhteen käyttäjätiliin.
Perus sisäkkäinen serialisointi ForeignKey:n avulla
Aloitetaan yksinkertaisella esimerkillä ForeignKey-suhteen serialisoinnista. Tarkastellaan näitä malleja:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
country = models.CharField(max_length=50, default='USA') # Lisätään maa-kenttä kansainvälistä kontekstia varten
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
publication_date = models.DateField()
def __str__(self):
return self.title
Serialisoidaksesi `Book`-mallin siihen liittyvällä `Author`-datalla, voimme käyttää sisäkkäistä serialisoijaa:
from rest_framework import serializers
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name', 'country']
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True) # Muutettu PrimaryKeyRelatedField:stä
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
Tässä esimerkissä `BookSerializer` sisältää `AuthorSerializer`-kentän. `read_only=True` tekee `author`-kentästä vain luku -tilassa olevan, estäen kirjailijan muokkaamisen kirjan päätepisteen kautta. Jos sinun on luotava tai päivitettävä kirjoja kirjailijatiedoilla, sinun on käsiteltävä kirjoitusoperaatioita eri tavalla (katso alla).
Nyt, kun serialisoit `Book`-objektin, JSON-tuloste sisältää täydelliset kirjailijatiedot sisäkkäin kirjan datassa:
{
"id": 1,
"title": "Linnunradan käsikirja liftareille",
"author": {
"id": 1,
"name": "Douglas Adams",
"country": "UK"
},
"publication_date": "1979-10-12"
}
ManyToManyField-suhteiden serialisointi
Tarkastellaan `ManyToManyField`-suhdetta. Oletetaan, että meillä on `Category`-malli ja kirja voi kuulua useisiin luokkiin.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
categories = models.ManyToManyField(Category, related_name='books')
publication_date = models.DateField()
def __str__(self):
return self.title
Voimme serialisoida kategoriat käyttämällä `serializers.StringRelatedField`- tai `serializers.PrimaryKeyRelatedField`-kenttiä tai luoda sisäkkäisen serialisoijan.
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
categories = CategorySerializer(many=True, read_only=True) # many=True on olennainen ManyToManyFieldille
class Meta:
model = Book
fields = ['id', 'title', 'author', 'categories', 'publication_date']
`many=True`-argumentti on ratkaisevan tärkeä serialisoitaessa `ManyToManyField`-kenttää. Se kertoo serialisoijalle odottavansa luetteloa kategoriaobjekteista. Tuloste näyttää tältä:
{
"id": 1,
"title": "Ylpeys ja ennakkoluulo",
"author": {
"id": 2,
"name": "Jane Austen",
"country": "UK"
},
"categories": [
{
"id": 1,
"name": "Klassinen kirjallisuus"
},
{
"id": 2,
"name": "Romantiikka"
}
],
"publication_date": "1813-01-28"
}
OneToOneField-suhteiden serialisointi
`OneToOneField`-suhteiden osalta lähestymistapa on samanlainen kuin ForeignKey, mutta on tärkeää käsitellä tapauksia, joissa liittyvää objektia ei välttämättä ole olemassa.
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField(blank=True)
location = models.CharField(max_length=100, blank=True, default='Global') # Lisätty sijainti kansainvälistä kontekstia varten
def __str__(self):
return self.user.username
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['id', 'bio', 'location']
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'profile']
Tuloste olisi:
{
"id": 1,
"username": "johndoe",
"email": "john.doe@example.com",
"profile": {
"id": 1,
"bio": "Ohjelmistoinsinööri.",
"location": "Lontoo, UK"
}
}
Kirjoitusoperaatioiden käsittely (luonti ja päivitys)
Yllä olevat esimerkit keskittyvät pääasiassa vain luku -tilassa olevaan serialisointiin. Salliaksesi liittyvien objektien luomisen tai päivittämisen, sinun on ohitettava serialisoijasi `create()`- ja `update()`-metodit.
Sisäkkäisten objektien luominen
Oletetaan, että haluat luoda uuden kirjan ja kirjailijan samanaikaisesti.
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
def create(self, validated_data):
author_data = validated_data.pop('author')
author = Author.objects.create(**author_data)
book = Book.objects.create(author=author, **validated_data)
return book
`create()`-metodissa puramme kirjailijatiedot, luomme uuden `Author`-objektin ja luomme sitten `Book`-objektin, joka liitetään juuri luotuun kirjailijaan.
Tärkeää: Sinun on käsiteltävä mahdolliset validointivirheet `author_data`-tiedoissa. Voit käyttää try-except-lohkoa ja nostaa `serializers.ValidationError`-virheen, jos kirjailijatiedot ovat virheelliset.
Sisäkkäisten objektien päivittäminen
Vastaavasti, päivittääksesi sekä kirjan että sen kirjailijan:
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
def update(self, instance, validated_data):
author_data = validated_data.pop('author', None)
if author_data:
author = instance.author
for attr, value in author_data.items():
setattr(author, attr, value)
author.save()
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
`update()`-metodissa haetaan olemassa oleva kirjailija, päivitetään sen attribuutit annettujen tietojen perusteella ja päivitetään sitten kirjan attribuutit. Jos `author_data`-tietoja ei ole annettu (eli kirjailijaa ei päivitetä), koodi ohittaa kirjailijan päivitysosion. `None`-oletusarvo `validated_data.pop('author', None)`-kohdassa on ratkaisevan tärkeä tapauksissa, joissa kirjailijatietoja ei sisällytetä päivityspyyntöön.
`PrimaryKeyRelatedField`-kentän käyttäminen
Sisäkkäisten serialisoijien sijaan voit käyttää `PrimaryKeyRelatedField`-kenttää esittämään suhteita käyttämällä liittyvän objektin pääavainta. Tämä on hyödyllistä, kun sinun tarvitsee vain viitata liittyvän objektin tunnukseen etkä halua serialisoida koko objektia.
class BookSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all())
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
Nyt `author`-kenttä sisältää kirjailijan tunnuksen:
{
"id": 1,
"title": "1984",
"author": 3, // Kirjailijan tunnus
"publication_date": "1949-06-08"
}
Luomista ja päivittämistä varten välität kirjailijan tunnuksen pyyntötiedoissa. `queryset=Author.objects.all()` varmistaa, että annettu tunnus on olemassa tietokannassa.
`HyperlinkedRelatedField`-kentän käyttäminen
`HyperlinkedRelatedField` esittää suhteita käyttämällä hyperlinkkejä liittyvän objektin API-päätepisteeseen. Tämä on yleistä hypermedia-APIt (HATEOAS).
class BookSerializer(serializers.ModelSerializer):
author = serializers.HyperlinkedRelatedField(view_name='author-detail', read_only=True)
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
`view_name`-argumentti määrittää sen näkymän nimen, joka käsittelee liittyvän objektin pyyntöjä (esim. `author-detail`). Sinun on määritettävä tämä näkymä `urls.py`-tiedostossasi.
Tuloste sisältää URL-osoitteen, joka osoittaa kirjailijan tietosivun päätepisteeseen:
{
"id": 1,
"title": "Uljas uusi maailma",
"author": "http://example.com/api/authors/4/",
"publication_date": "1932-01-01"
}
Edistyneet tekniikat ja huomioitavat asiat
- `depth`-vaihtoehto: `ModelSerializer`-kohdassa voit käyttää `depth`-vaihtoehtoa luodaksesi automaattisesti sisäkkäisiä serialisoijia ForeignKey-suhteille tiettyyn syvyyteen asti. `depth`-vaihtoehdon käyttäminen voi kuitenkin johtaa suorituskykyongelmiin, jos suhteet ovat monimutkaisia, joten on yleensä suositeltavaa määrittää serialisoijat eksplisiittisesti.
- `SerializerMethodField`: Käytä `SerializerMethodField`-kenttää luodaksesi mukautettua serialisointilogiikkaa liittyville tiedoille. Tämä on hyödyllistä, kun sinun on muotoiltava tiedot tietyllä tavalla tai sisällytettävä laskennallisia arvoja. Voit esimerkiksi näyttää kirjailijan koko nimen eri järjestyksessä alueen perusteella. Monissa Aasian kulttuureissa sukunimi tulee ennen etunimeä.
- Mukautetun esityksen luominen: Ohita serialisoijasi `to_representation()`-metodi mukauttaaksesi liittyvien tietojen esitystapaa.
- Suorituskyvyn optimointi: Monimutkaisissa suhteissa ja suurissa tietokokonaisuuksissa käytä tekniikoita, kuten select_related ja prefetch_related, optimoidaksesi tietokantakyselyt ja vähentääksesi tietokantahittien määrää. Tämä on erityisen tärkeää APIille, jotka palvelevat globaaleja käyttäjiä, joilla saattaa olla hitaammat yhteydet.
- Tyhjien arvojen käsittely: Ota huomioon, miten tyhjiä arvoja käsitellään serialisoijissasi, erityisesti kun käsitellään valinnaisia suhteita. Käytä `allow_null=True`-asetusta serialisoijakentissäsi tarvittaessa.
- Validointi: Ota käyttöön vankka validointi varmistaaksesi tietojen eheyden, erityisesti kun luodaan tai päivitetään liittyviä objekteja. Harkitse mukautettujen validoijien käyttämistä liiketoimintasääntöjen valvomiseksi. Esimerkiksi kirjan julkaisupäivämäärä ei saa olla tulevaisuudessa.
- Kansainvälistäminen ja lokalisointi (i18n/l10n): Harkitse, miten tietosi näytetään eri kielillä ja alueilla. Muotoile päivämäärät, numerot ja valuutat asianmukaisesti käyttäjän alueen mukaan. Tallenna kansainvälistettävät merkkijonot malleihisi ja serialisoijiisi.
Serializer-suhteiden parhaat käytännöt
- Pidä serialisoijat kohdennettuina: Jokaisen serialisoijan tulisi olla vastuussa tietyn mallin tai läheisesti liittyvän datakokonaisuuden serialisoinnista. Vältä liian monimutkaisten serialisoijien luomista.
- Käytä eksplisiittisiä serialisoijia: Vältä liiallista luottamista `depth`-vaihtoehtoon. Määritä eksplisiittiset serialisoijat jokaiselle liittyvälle mallille saadaksesi enemmän hallintaa serialisointiprosessiin.
- Testaa perusteellisesti: Kirjoita yksikkötestejä varmistaaksesi, että serialisoijasi serialisoivat ja deserialisoivat tiedot oikein, erityisesti kun käsitellään monimutkaisia suhteita.
- Dokumentoi API:si: Dokumentoi selkeästi API-päätepisteet ja datamuodot, joita ne odottavat ja palauttavat. Käytä työkaluja, kuten Swagger tai OpenAPI, luodaksesi interaktiivisen API-dokumentaation.
- Harkitse API-versiointia: Kun APIsi kehittyy, käytä versiointia säilyttääksesi yhteensopivuuden olemassa olevien asiakkaiden kanssa. Näin voit ottaa käyttöön rikkovia muutoksia vaikuttamatta vanhempiin sovelluksiin.
- Seuraa suorituskykyä: Seuraa APIn suorituskykyä ja tunnista serialisoijasuhteisiin liittyvät pullonkaulat. Käytä profilointityökaluja tietokantakyselyjen ja serialisointilogiikan optimoimiseksi.
Johtopäätös
Serializer-suhteiden hallinta Django REST Frameworkissa on olennaista vankkojen ja tehokkaiden web-APIen rakentamisessa. Ymmärtämällä erilaiset suhdetyypit ja DRF-serialisoijien erilaiset vaihtoehdot, voit tehokkaasti serialisoida ja deserialisoida sisäkkäisiä objekteja, käsitellä kirjoitusoperaatioita ja optimoida APIsi suorituskykyä varten. Muista ottaa huomioon kansainvälistäminen ja lokalisointi suunnitellessasi APIasi varmistaaksesi, että se on maailmanlaajuisen yleisön käytettävissä. Perusteellinen testaus ja selkeä dokumentointi ovat avain APIn pitkäaikaisen ylläpidettävyyden ja käytettävyyden varmistamiseen.