DEV Community

Maximiliano Burgos
Maximiliano Burgos

Posted on

Diario de Python | #13. Color Choice: emitir votos

Es increíble que un proyecto tan simple como éste, tenga tantas posibilidades de probar cosas técnicas. De hecho, lo elegí justamente con eso: menos vueltas del lado del negocio, más jugo que le podemos sacar a nivel técnico.

Por supuesto, esto aplica a mi proyecto de una forma muy particular: hay otros casos donde el negocio implica desafíos técnicos, y esto está muy bien, pero yo me conformo con lo que estamos haciendo ahora.

Antes de continuar

Haciendo unas pruebas, dado que estoy utilizando la última versión de DRF, cuando hice un GET a "api/colors" me devolvió lo siguiente:

Creating a ModelSerializer without either the 'fields' attribute or the 'exclude' attribute has been deprecated since 3.3.0, and is now disallowed. Add an explicit fields = '__all__' to the NestedSerializer serializer.
Enter fullscreen mode Exit fullscreen mode

Para resolver esto, tal como dice el error, agregué "fields = 'all'" a mis serializadores:

class ColorSerializer(ModelSerializer):
    class Meta:
        model = Color
        fields = '__all__'


class VoteSerializer(ModelSerializer):
    class Meta:
        model = Vote
        fields = '__all__'
        depth = 1
Enter fullscreen mode Exit fullscreen mode

Seguimos con la transmisión habitual 😊

Métodos del ModelViewSet

Para poder modificar a VotesViewSet, necesitamos entender cómo cambiar el comportamiento de los métodos de su padre, ModelViewSet:

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass
Enter fullscreen mode Exit fullscreen mode

De esta parte nos interesan los comentarios:

  • create(): crea un nuevo voto.
  • retrieve(): trae un voto por su id.
  • update() / partial_update(): modifica un voto total o parcialmente.
  • destroy(): elimina un voto.
  • list(): trae todos los votos.

Esto se parece los endpoints que vimos hace un par de artículos, justamente porque son estos quieren apuntan a dichos métodos.

Generar un Vote

Siguiendo el flujo, ahora deberíamos poder crear un voto nuevo basado en el usuario de la sesión. Primero, restringimos el acceso a los endpoints de Vote mediante Auth Token:

class VoteViewSet(viewsets.ModelViewSet):
    (...)
    authentication_classes = [TokenAuthentication]
Enter fullscreen mode Exit fullscreen mode

Ahora cualquier método relacionado con Vote, va a requerir del token que generamos anteriormente. Una vez lo tenemos, lo agregamos como header Auth:

Postman Header Auth

Un Vote tiene asociado un Color dentro, por lo cual debemos especificarlo en VoteSerializer:

class VoteSerializer(ModelSerializer):
    color = ColorSerializer(many=False, read_only=True)
Enter fullscreen mode Exit fullscreen mode

Luego debemos redefinir nuestro método create de VoteViewSet:

def create(self, request, *args, **kwargs):
    serializer = VoteSerializer(data=request.data)

    if serializer.is_valid():
        color = Color(**request.data['color'])

        serializer.save(user=request.user, color=color)
        return Response({
            "success": True,
            "message": "Voto emitido exitosamente.",
            "vote": serializer.data
        }, status=status.HTTP_201_CREATED)
    else:
        return Response({
            "success": False,
            "message": "Error al emitir Voto!",
            "vote": serializer.errors
        }, status=status.HTTP_400_BAD_REQUEST)
Enter fullscreen mode Exit fullscreen mode

Pueden notar que en request recibo data y user. El atributo user se genera como un objeto interno que ya resolvió nuestro autenticador con token internamente.

En el caso de data, debemos enviarlo por JSON Body de la siguiente manera:

{
    "color": {
        "id": 1,
        "name": "azul",
        "hexa": "#360FFF"
    }
}
Enter fullscreen mode Exit fullscreen mode

El objeto color que estamos indicando lo podemos obtener mediante el endpoint "GET api/colors".

Otro detalle importante: para obtener y llevar el campo id, debemos declararlo en nuestro serializador:

class ColorSerializer(ModelSerializer):
    id = IntegerField()
Enter fullscreen mode Exit fullscreen mode

Una vez emitimos el voto, obtendremos una respuesta como esta:

{
    "success": true,
    "message": "Voto emitido exitosamente.",
    "vote": {
        "id": 1,
        "color": {
            "id": 1,
            "name": "azul",
            "hexa": "#360FFF"
        },
        "user": {
            "id": 1,
            "password": "pbkdf2_sha256$600000$MTt324mR10bsmpz5WVycFV$B0Lj6QFYWgUKI2txcsxdnu4sW9T0ZSU/OB67JThJfdk=",
            "last_login": "2023-04-12T14:13:56.147422Z",
            "is_superuser": true,
            "username": "admin",
            "first_name": "",
            "last_name": "",
            "email": "",
            "is_staff": true,
            "is_active": true,
            "date_joined": "2023-04-12T14:13:42.051901Z",
            "groups": [],
            "user_permissions": []
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Serializador para User

La respuesta anterior es correcta, pero nos está devolviendo demasiada información del usuario; incluso la sensible. Necesitamos arreglar esto mediante su propio serializer:

class UserSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'first_name', 'last_name']
Enter fullscreen mode Exit fullscreen mode

También debemos incluirlo en VoteSerializer:

user = UserSerializer(many=False, read_only=True)
Enter fullscreen mode Exit fullscreen mode

Ahora, si emitimos un voto (eliminé el anterior por django-admin, recuerden que solo se puede emitir uno por usuario), nos devolverá esto:

{
    "success": true,
    "message": "Voto emitido exitosamente.",
    "vote": {
        "id": 2,
        "color": {
            "id": 1,
            "name": "azul",
            "hexa": "#360FFF"
        },
        "user": {
            "username": "admin",
            "first_name": "",
            "last_name": ""
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusiones

No se va a notar en el artículo, pero estuve peleando muy fuerte con los NestedSerializers. Estos son los serializadores que contienen otros, como pasa en el caso de Color dentro de Vote.

No obstante, con la posibilidad de votar implementada, ahora solo queda validar que dichos votos existan para un determinado usuario.

Pero eso será, queridos lectores, en el siguiente capítulo. 😁

Top comments (0)