Crea una REST API con Python e Django REST Framework

Pubblicato da Michele Saba

In questa lezione impareremo ad utilizzare Python per creare una Web API per una Job Board, ovvero un sito specializzato in cui vengono mostrate offerte di lavoro da parte di varie aziende che possono postare nuovi annunci in autonomia. I client potranno comunicare con la Web API tramite due endpoint URL:

  • api/jobs/ - accetta metodi HTTP GET e POST, permettendo di ottenere un elenco con gli annunci già presenti nel database e aggiungerne di nuovi.
  • api/jobs/int:pk/ - accetta metodi HTTP GET, PUT e DELETE, permettendo di ottenere i dettagli di un singolo annuncio e aggiornarlo o cancellarlo.

Per la creazione della nostra Web API, utilizzeremo Django e Django REST Framework.

Django è uno dei framework web più popolari e utilizzati nel mondo di Python: oltre ad essere altamente configurabile e personalizzabile, fornisce una serie di componenti predefiniti che semplificano la creazione di funzionalità comuni come l'autenticazione degli utenti, la gestione dei modelli di database e la comunicazione con API esterne.

Se non conoscete Django e vorreste addentrarvi nello sviluppo web, potete seguire la Guida Pratica e Completa a Python, Django e Bootstrap, il nostro corso professionale completo in cui parliamo nei dettagli di Python, HTML, CSS, Bootstrap e di e come mettere online un sito!

Django REST Framework (DRF) è una libreria per Django che fornisce strumenti per la creazione di REST API.


Creiamo un nuovo Progetto Django

Installiamo Django in un nuovo ambiente virtuale e creiamo un nuovo progetto chiamato job_board. Creiamo una nuova applicazione chiamata jobs e nel file models.py definiamo un modello che rappresenti le offerte di lavoro aggiungendo la classe JobOffer, in cui definiamo come campi tutte le informazioni relative ad una singola offerta di lavoro:

from django.db import models


class JobOffer(models.Model):
    company_name = models.CharField(max_length=50)
    company_email = models.EmailField()
    job_title = models.CharField(max_length=60)
    job_description = models.TextField()
    salary = models.PositiveIntegerField()
    city = models.CharField(max_length=35)
    state = models.CharField(max_length=35)
    created_at = models.DateField(auto_now_add=True)
    available = models.BooleanField(default=True)

    def __str__(self):
        return self.company_name

Registriamo il modello all'interno del file admin.py:

from django.contrib import admin
from jobs.models import JobOffer

admin.site.register(JobOffer)

E in settings.py andiamo ad installare la nostra app aggiungendola all’elenco INSTALLED_APPS:

INSTALLED_APPS = [
    # ...

    'jobs'

Effettuiamo la migrazione per applicare le nostre modifiche al database e all’interno del pannello di Django Admin creiamo delle istanze di offerta di lavoro di job offer, ciascuna con le caratteristiche definite nel modello:

Offerte di lavoro DRF

Per ogni istanza definiamo il nome dell'azienda, un indirizzo email, il nome della posizione richiesta, una descrizione del lavoro, il salario, la città, lo stato e infine il flag impostato su “Available”, che ci informa se questo lavoro sia ancora disponibile o meno:

Offerta di lavoro DRF


Installiamo Django REST Framework e creiamo un Serializer

Installiamo anche Django REST Framework nel nostro ambiente virtuale:

pip install djangorestframework

Aggiungiamo il framework alla variabile INSTALLED_APPS nel file settings.py:

INSTALLED_APPS = [
    # ...

    'rest_framework',
    'jobs'
]

Il primo passo da compiere per andare a creare la nostra Web API è creare un Serializer che corrisponde al nostro modello JobOffer. All’interno dell’applicazione jobs creiamo una cartella chiamata api e al suo interno creiamo il file serializers.py, importiamo serializers da Django REST Framework e il modello JobOffer da models.py.

from rest_framework import serializers
from jobs.models import JobOffer

I Serializers di Django REST Framework

In DRF, i Serializers permettono la conversione dei modelli di Django in tipi di dato nativi di Python, che poi possono essere convertiti in formati come JSON o XML, comunemente utilizzati per lo scambio di dati tra client e server.

Questo processo prende il nome di serializzazione (in inglese serialization), e i Serializer possono anche effettuare il processo opposto, ovvero la deserializzazione (in inglese deserialization), in cui i dati ricevuti dal client in un certo formato vengono trasformati in oggetti utilizzabili da Django.

I serializer sono una componente fondamentale di DRF: il loro utilizzo semplifica notevolmente la creazione e la gestione di REST API in Django, consentendo di concentrarsi sulla logica dell'applicazione e delegando la gestione della serializzazione e della deserializzazione dei dati a DRF.

Noi utilizzeremo ModelSerializer, che ci fornisce gran parte del codice necessario per poter lavorare con dei modelli definiti nel file models.py in maniera automatica: utilizzerà i campi del nostro modello per popolare quelli del serializer e creerà una versione base dei metodi create e update che sono necessari per la creazione e l'aggiornamento delle nostre istanze.

Creiamo quindi la classe JobOfferSerializer e facciamo in modo che erediti da serializer.ModelSerializer. Al suo interno definiamo la classe meta, specifichiamo il modello e i campi:

class JobOfferSerializer(serializers.ModelSerializer):

    class Meta:
        model = JobOffer
        fields = "__all__"


Creiamo la View per il primo endpoint

Abbiamo detto che dobbiamo creare due endpoint: cominciamo a con il creare il file views.py all’interno della cartella api, che ci servirà per ottenere un elenco degli annunci nel database e aggiungerne di nuovi. Le nostre view andranno a riflettere quelle che sono le richieste che supponiamo verranno effettuate tramite gli endpoint. Aggiungiamo i seguenti import:

from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView

from jobs.models import JobOffer
from jobs.api.serializers import JobOfferSerializer

Iniziamo la scrittura delle nostre view creando la classe JobOfferListCreateAPIView, che estende la classe APIView che abbiamo importato. Al suo interno andiamo a definire dei metodi il cui nome è lo stesso della richiesta che vogliamo venga effettuata verso l’endpoint che collega l'utente all’API.

Creiamo un metodo get()

Iniziamo dalla scrittura del metodo get, che accetta self e request, definiamo la queryset delle offerte di lavoro utilizzando la funzione filter me mostrare solo quelle ancora disponibili. Con la variabile serializer inizializziamo JobOfferSerializer a cui passiamo jobs e many e facciamo in modo che il metodo restituisca la risposta Response.

class JobOfferListCreateAPIView(APIView):

    def get(self, request):
        jobs = JobOffer.objects.filter(available=True)
        serializer = JobOfferSerializer(jobs, many=True)
        return Response(serializer.data)

Creiamo un metodo post()

Creiamo anche il metodo post inzializzando anche in questo caso il nostro serializer passandogli data tramite request e con un’istruzione condizionale verifichiamo se i dati sono validi: in tal caso richiamiamo il metodo save di serializer e restituiamo come Response il codice di stato HTTP 201 se i dati sono validi o 400 se non lo sono.

class JobOfferListCreateAPIView(APIView):

    def get(self, request):
        # ...

    def post(self, request):
        serializer = JobOfferSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Aggiungiamo il primo endpoint nel file urls.py

Creiamo il file urls.py all’interno della cartella api per definire il primo dei nostri due endpoint. Importiamo path da django.urls e la nostra View:

from django.urls import path
from jobs.api.views import JobOfferDetailAPIView

Definiamo gli urlpatterns per la lista delle offerte di lavoro disponibili:

urlpatterns = [
    path("jobs/",
         JobOfferListCreateAPIView.as_view(),
         name="job-list"),
]

Andiamo ad includere questi nuovi URL all'interno del file urls.py del progetto principale:

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path("api/", include("jobs.api.urls"))
]

Verifichiamo il funzionamento del primo endpoint

A questo punto siamo quindi pronti a a testare il nostro endpoint: andiamo all’indirizzo http://127.0.0.1:8000/api/jobs/ e vedremo che abbiamo ottenuto un elenco con le offerte di lavoro che al momento sono presenti nel nostro database:

Job Offer List Create Api DRF

Proviamo a creare una nuova offerta di lavoro copiando una dei quelle presenti, rimuovendo il created_at, l’id e modificando il nome e l’email dell’azienda:

Creare Nuova Offerta di Lavoro DRF

Premiamo il tasto Post e vediamo che è stato creato il nuovo annuncio di lavoro, che ora sarà incluso nella lista insieme agli altri.

Crea Offerta Di Lavoro Codice 201 DRF


Creiamo la View per il secondo endpoint

Adesso dobbiamo occuparci della creazione del secondo endpoint che ci permetta di ottenere i dati di una specifica istanza di offerta di lavoro, di aggiornarla o cancellarla.

Creiamo un metodo get_object()

Apriamo il file view.py dalla cartella api e creiamo una nuova classe chiamata JobOfferDetailAPIView a cui passiamo APIView.

Per ottenere i dati di una specifica istanza, definiamo un metodo get_object a cui passiamo self e la chiave primaria e assegniamo alla variabile job la funzione get_object_or_404 a cui passiamo il nostro modello JobOffer e la primary key. Infine restituiamo job con return.

class JobOfferDetailAPIView(APIView):

    def get_object(self, pk):
        job = get_object_or_404(JobOffer, pk=pk)
        return job

Creiamo un metodo get()

Creiamo un metodo get in modo analogo a quello che abbiamo fatto con l’altra nostra View, ovvero classe JobOfferListCreateAPIView, ma utilizzando il nostro nuovo metodo get_object per la definizione della variabile job, e passandogli come parametro la chiave primaria:

class JobOfferDetailAPIView(APIView):

    # ...

    def get(self, request, pk):
        job = self.get_object(pk)
        serializer = JobOfferSerializer(job)
        return Response(serializer.data)

Creiamo un metodo put()

Definiamo un metodo put similmente a come abbiamo definito post per l’altra View. Questo metodo ci dovrà permettere di aggiornare un’istanza già presente: otteniamola tramite la variabile job e inizializziamo il serializer, ma questa volta passiamogli come dati anche quelli della richiesta, che ci servono per sapere in che modo aggiornare una nostra istanza.

Verifichiamo che i dati passati siano quindi corretti e in tal caso restituiamo il nostro Response con i dati associati, se non sono corretti passiamo gli errori forniti dal serializer e chiaramente anche un codice di stato HTTP 400.

class JobOfferDetailAPIView(APIView):

    # ...

    def put(self, request, pk):
        job = self.get_object(pk)
        serializer = JobOfferSerializer(job, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Creiamo un metodo delete()

Definiamo l’ultimo metodo per la nostra View: delete. Anche questo accetterà gli stessi parametri degli altri metodi, con la differenza che semplicemente cancellerà l’oggetto job. Il nostro Response in questo caso sarà il codice di stato 204.

class JobOfferDetailAPIView(APIView):

    # ...

    def delete(self, request, pk):
        job = self.get_object(pk)
        job.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Aggiungiamo il secondo endpoint nel file urls.py

Andiamo ora a definire quindi l'endpoint URL per la nostra nuova View. Apriamo il file urls.py della cartella api e aggiungiamo il nuovo URL, costruito con la chiave primaria.

urlpatterns = [
    # ...

    path("jobs/<int:pk>/",
         JobOfferDetailAPIView.as_view(),
         name="job-detail"),
]

Verifichiamo il funzionamento del secondo endpoint

Apriamo il browser e verifichiamo il nuovo endpoint. Ipotizziamo che l’offerta di lavoro non sia più disponibile perché è stato trovato un candidato e rendiamo “Available” False. Se premiamo il tasto Put, vedremo che lla nostra istanza è stata aggiornata.

Offerta Di Lavoro Non Disponibile DRF


Adesso la nostra REST API è completa!


Ho una notizia che penso potrebbe interessarti!

Se hai letto questo articolo è ragionevole assumere che tu stia iniziando il tuo percorso nel fantastico mondo delle Web API. Ti interesserà allora sapere che questa lezione è parte del nostro corso Guida per Sviluppatori a Django REST Framework e Vue JS. Il corso si concentra sullo sviluppo di REST API e applicazioni web complete, utilizzando Django REST Framework per gestire il backend e il Vue.js per lo sviluppo del lato frontend. L'obiettivo del corso è fornirti una solida comprensione di come utilizzare questi due framework insieme per creare applicazioni web moderne, performanti, sicure e scalabili. Scopri di più e inizia subito!


Vuoi imparare Python come un/a professionista? Dai uno sguardo ai nostri