Django Ninja és una llibreria per a fer APIs alternativa a la clàssica Django REST Framework (que podeu veure a l'article Django API) i fortament inspirada en la molt adoptada recentment FastAPI.
El principal avantatge és certa simplicitat per crear els endpoints.
Referències:
És relativament fàcil acostar-se a Django Ninja amb la seva documentació:
api.py
: https://django-ninja.dev/tutorial/
Destaquem dos mètodes d'autenticació molt estandarditzats:
curl http://localhost:8000/api/token/ -u manolo:pass123
curl -H 'Authorization: Bearer 1d895b9062512a731b1e5667bd034da3' http://localhost:8000/api/llibres
Es poden implementar les dues amb aquest codi:
from django.contrib.auth import authenticate as django_authenticate from ninja import NinjaAPI, Schema from ninja.security import HttpBasicAuth, HttpBearer from typing import List, Optional from .models import * import secrets api = NinjaAPI() # Autenticació bàsica class BasicAuth(HttpBasicAuth): def authenticate(self, request, username, password): user = django_authenticate(username=username, password=password) if user: # Genera un token simple token = secrets.token_hex(16) user.auth_token = token user.save() return token return None # Autenticació per Token Bearer class AuthBearer(HttpBearer): def authenticate(self, request, token): try: user = Usuari.objects.get(auth_token=token) return user except Usuari.DoesNotExist: return None # Endpoint per obtenir un token, accés amb BasicAuth # amb o sense "trailing slash" @api.get("/token", auth=BasicAuth()) @api.get("/token/", auth=BasicAuth()) def obtenir_token(request): return {"token": request.auth} # Exemple d'endpoint per llistar els llibres, accés amb TokenAuth class LlibreOut(Schema): id: int titol: str editorial: Optional[str] @api.get("/llibres/", response=List[LlibreOut], auth=AuthBearer()) def get_llibres(request): qs = Llibre.objects.all() return qs
A part, també caldrà afegir la columna auth_token
al model Usuari
que cal personalitzar:
from django.contrib.auth.models import AbstractUser class Usuari(AbstractUser): auth_token = models.CharField(max_length=32,blank=True,null=True) # + altres atributs que es vulguin afegir...
I a settings.py
:
AUTH_USER_MODEL = 'myapp.Usuari'
Per tal que els canvis es guardin a la base de dades caldrà crear i executar les migracions adequades:
(env) $ ./manage.py makemigrations (env) $ ./manage.py migrate
Afegir canvis en el model d'usuari quan un projecte ja té dades de producció és problemàtic, com a mínim. Es recomana sempre crear un model d'usuari personalitzat (derivat d'AbstractUser) des del principi del projecte per poder afegir funcionalitats com aquesta (afegir token a l'usuari) quan sigui necessari.
Si no tenim dades de producció, el més fàcil és esborrar la base de dades, esborrar tots els arxius de migració, i tornar a crear-los amb:
(env) $ ./manage.py makemigrations <nom_app>
Una possible alternativa, per tal de no haver de crear un model d'usuari personalitzat, és crear una taula de tokens a part, i relacionar-la amb el User
estàndard de Django. Igualment es sol recomanar prioritàriament particularitzar el model d'usuari, ja que les situacions en les que interessarà ampliar-lo son molt comunes.
El típic exemple de request amb Basic Authentication seria:
curl -u admin:admin123 http://localhost:8000/api/users/
De vegades ens pot interessar una alternativa enviant les dades codificades en base64 en els headers:
curl -H 'Authorization:Basic YWRtaW46YWRtaW4xMjM=' http://localhost:8000/api/users/
Per obtenir el hash en base64 es pot fer:
echo -n "admin:admin123" | base64
Per aconseguir un pretty print del JSON es pot fer amb la utilitat jq
curl -u admin:admin123 http://localhost:8000/api/users/ | jq
Per instal·lar jq es pot fer:
sudo apt install jq