Diario de Python | #9. Color Choice: Volantazo
Tags: devjournal, python, spanish
Published: 2023-04-11 13:32:33

Este diario es el fruto de mis confesiones técnicas, y aca va una: estuve inmerso en [el curso de desarrollo web con Python y Django](https://dev.to/maxwellnewage/nuevo-curso-de-python-con-django-bcd); y esto me alejó de FastAPI, al menos por un tiempo.

Además encontré en Django una forma más robusta de trabajar, tanto en una web con su frontend como en la API que puede exponer con sus métodos y sus serializadores.

## Refactor del Proyecto
Por un lado, borré las carpetas y dependencias de FastAPI y las reemplacé por las de Django y DRF (Django Rest Framework). Pueden acceder al proyecto final en mi [repositorio](https://github.com/maxwellnewage/python-color-choice-api).

Por otro, armé una app llamada "api", la cual contiene todos los modelos, serializers, views y demás.

## Los modelos
Tanto para Vote como Color, definí ambos en models.py:

```python
class Color(models.Model):
    name = models.CharField(max_length=10)

    def __str__(self):
        return self.name


class Vote(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, unique=True)
    color = models.ForeignKey(Color, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.user} voted to {self.color}"
```

## Django Admin
Para poder agregarlos mediante el admin de Django, los incluí en admin.py:

```python
from .models import Vote, Color

admin.site.register([Vote, Color])
```

## Serializers
DRF tiene un componente que permite controlar la información de los modelos que devolveremos en la API. En serializers.py definí los dos que necesitaba:

```python
from rest_framework.serializers import ModelSerializer
from .models import Vote, Color


class ColorSerializer(ModelSerializer):
    class Meta:
        model = Color


class VoteSerializer(ModelSerializer):
    class Meta:
        model = Vote
        depth = 1
```

El atributo _depth = 1_ permite que la respuesta de Vote contenga dentro a Color. Si no ponía esta propiedad, solo devolvía el id que lo relaciona. Esto es posible por lo que definimos previamente en el modelo de Vote:

```python
color = models.ForeignKey(Color, on_delete=models.CASCADE)
```

## Viewsets
Un ViewSet es una clase que agrupa lógica de vistas relacionadas en un solo lugar. Es una abstracción de alto nivel que permite definir un conjunto de vistas (GET, POST, PUT, DELETE, entre otras) para un modelo determinado con una única clase.

Posee dos atributos básicos y obligatorios, los cuales son _queryset_, que define los datos con los que vamos a trabajar; y _serializer_class_, el serializador que aplicará a los mismos.

En mi caso yo tenía dos modelos, dos serializadores, y por supuesto, necesitaba dos Viewsets:

```python
class ColorViewSet(viewsets.ModelViewSet):
    queryset = Color.objects.all()
    serializer_class = ColorSerializer


class VoteViewSet(viewsets.ModelViewSet):
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer
```

Por supuesto, esto no iba a funcionar sin mapear las rutas adecuadas.

## Manejo de rutas
En primer lugar creamos un objeto _router_, el cual servirá para registrar mediante el método _register_ cada ruta con su ViewSet correspondiente.

Por ultimo, generamos la famosa lista _urlpatterns_ y le pasamos _router.urls_, el cual contendrá el array de rutas generadas automáticamente por el ViewSet.

```python
from rest_framework import routers
from .views import ColorViewSet, VoteViewSet


router = routers.DefaultRouter()

router.register('colors', ColorViewSet, 'colors')
router.register('votes', VoteViewSet, 'votes')

urlpatterns = router.urls
```

También debemos incluir la ruta en _urls.py_ del proyecto:

```python
from django.urls import path, include

urlpatterns = [
    (...),
    path('api/', include('api.urls'))
]
```

## Mucho código, demasiados componentes
Puede que al principio asuste tanta implementación, especialmente si venís de Flask o de desarrollar APIs en otras tecnologías. Pero la realidad es que todo en Django tiene un propósito y una explicación, y voy a detallarlo ahora:
- Los modelos son nuestra ventana a la BD, la representación más fiel a nuestro modelo de persistencia utilizando ORM.
- Django Admin es opcional, pero me parece cómodo para no depender de implementar toda la API para poder probar la lógica de los modelos.
- Los serializadores son muy potentes, y creeme que en este ejemplo todavía no los estoy exprimiendo. Son como un middleware entre nuestros modelos y la respuesta final en la API.
- Los Viewsets nos van a generar todos los métodos de un CRUD sin que nosotros tengamos que hacerlo a mano.
- Y finalmente las rutas nos van a permitir vincular todo esto.

En los próximos artículos voy a poner esto en marcha!