API Django

Ejemplo Simple

  • Sobre Django

  • Preparar entorno

  • Crear Proyecto

  • Crear API

  • Documentar API

  • Pruebas

Django

Django makes it easier to build better web apps more quickly and with less code.

https://www.djangoproject.com

Sobre Django

  • Escrito en python
  • Lanzado 2005
  • Github
    • Starts - 59k
    • Used By - 728k
    • Contributors - 2k
  • Buena documentacion
  • StackOverflow - 93k

Preparar entorno 

Recursos usados

Crear proyecto

django-admin startproject inder
cd inder
django-admin startapp api
python manage.py migrate
python manage.py createsuperuser --email admin@example.com --username admin

Ejemplo online

Usuario: Admin

Contraseña: Admin123

Crear API

You keep using that word "REST". I do not think it means what you think it means.

Mike Amundsen, REST fest 2012 keynote.

Models

#models.py

from django.db import models

class Sport(models.Model):
    name = models.CharField(max_length=60)
    alias = models.CharField(max_length=60)
    def __str__(self):
        return self.name

class Athlete(models.Model):
    name = models.CharField(max_length=60)
    last_name = models.CharField(max_length=60)
    age = models.IntegerField()
    sport = models.ForeignKey(Sport, on_delete=models.CASCADE)
    def __str__(self):
        return self.name + self.last_name

Serializer

#serializers.py

from rest_framework import serializers

from .models import Sport, Athlete

class SportSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Sport
        fields = ('name', 'alias')

class AthleteSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Athlete
        fields = ('name', 'last_name', 'age', 'sport')

View

# views.py

from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend

from .serializers import SportSerializer, AthleteSerializer
from .models import Sport, Athlete


class SportViewSet(viewsets.ModelViewSet):
    queryset = Sport.objects.all().order_by('name')
    serializer_class = SportSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name']

class AthleteViewSet(viewsets.ModelViewSet):
    queryset = Athlete.objects.all().order_by('name')
    serializer_class = AthleteSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name']

URLs

# inder/urls.py

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

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

from django.urls import include, path
from rest_framework import routers
from . import views

router = routers.DefaultRouter()
router.register(r'sports', views.SportViewSet)
router.register(r'athletes', views.AthleteViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

Settings

# inder/settings.py

...

INSTALLED_APPS = [
  	....
    'django_filters',
    'rest_framework'
]



REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

Documentar API

A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state.

Roy Fielding, REST APIs must be hypertext driven

Se puede usar

  • Swagger UI
  • ReDoc
  • drf-yasg
  • drf-spectacular
  • Nativa 
pip install markdown
# views.py

class SportViewSet(viewsets.ModelViewSet):
    """
    Deportes que se gestionan en la **aplicación**.

    Escrita en markdowan y se se puede ampliar más. 
    Leer [Documenting your API](https://www.django-rest-framework.org/topics/documenting-your-api/).
    """

    queryset = Sport.objects.all().order_by('name')
    ...

Nativa

Pruebas

Code without tests is broken as designed.

Jacob Kaplan-Moss

# api/test/tests_sport.py
class SportTestCase(TestCase):
    def setUp(self):
        self.access_token = result['access_token']
        self.user = user

    def test_create_sport(self):
        client = APIClient()

        test_sport = {
            'name': 'taekwondo',
            'alias': 'tw2'
        }

        response = client.post(
            '/api/sports/',
            test_sport,
            format='json'
        )

        result = json.loads(response.content)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('name', result)

        self.assertEqual(result, test_sport)

   
./manage.py test api
/var/app/src # ./manage.py test api 
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.018s

OK
Destroying test database for alias 'default'...

Ejecutar purebas

Resultado Correcto

/var/app/src # ./manage.py test api 
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E.E
======================================================================
ERROR: test_create_sport (api.test.tests_sport.SportTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/var/app/src/api/test/tests_sport.py", line 33, in test_create_sport

Resultado incorrecto

Tiempos

  • Preparar entorno de trabajo ( 25m ) 
  • Crear API de ejemplo ( 30m )
  • Documentar API ( 20m )
  • Crear Pruebas ( 24m )
  • Despliegue ( 25m )

Muchas gracias

API Django

By ruby232

API Django

  • 73