Personalización de permisos de nivel de objeto: Django REST Framework

En este artículo, discutiremos cómo personalizar los permisos de nivel de objeto en Django REST Framework. Para personalizar las clases de permisos en Django REST Framework, debemos heredar la clase rest_framework.permissions.BasePermission e implementar uno o ambos de los siguientes métodos:

  • .has_permission(auto, solicitud, vista)
  • .has_object_permission(self, request, view, obj)

Si observamos nuestro modelo de robot mencionado en la API navegable en Django REST Framework , podemos notar que cualquier usuario autenticado puede eliminar los robots incluso después de configurar las políticas de permisos en nuestro servicio web RESTFul. Aquí viene la importancia de personalizar el permiso a nivel de objeto para que solo el propietario de un robot pueda actualizar o eliminar un robot existente. 

Creación de una clase de permiso personalizada

Vaya a la carpeta de robots que tiene el archivo views.py y cree un nuevo archivo llamado custompermission.py . Puede escribir el siguiente código en el nuevo archivo.

Python3

from rest_framework import permissions
  
  
class IsCurrentUserOwnerOrReadOnly(permissions.BasePermission):
    
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            
            # The method is a safe method
            return True
            
        else:
            # The method isn't a safe method
            # Only owners are granted permissions for unsafe methods
            return obj.owner == request.user

IsCurrentUserOwnerOrReadOnly hereda de la clase BasePermission y anula el método has_object_permission . El método devuelve un valor booleano, que indica si se debe otorgar el permiso o no. has_object_permission diferencia los métodos seguros e inseguros, y solo los propietarios tienen permiso para los métodos inseguros.

Agreguemos el campo de propietario al archivo robots/models.py.

owner = models.ForeignKey(
        'auth.User',
        related_name= 'robots',
        on_delete=models.CASCADE
    )

La clase Robot tiene el siguiente aspecto:

Python3

class Robot(models.Model):
    CURRENCY_CHOICES = (
        ('INR', 'Indian Rupee'),
        ('USD', 'US Dollar'),
        ('EUR', 'Euro'),
    )
  
    name = models.CharField(max_length=150, unique=True)
      
    robot_category = models.ForeignKey(
        RobotCategory,
        related_name='robots',
        on_delete=models.CASCADE)
      
    manufacturer = models.ForeignKey(
        Manufacturer,
        related_name='robots',
        on_delete=models.CASCADE)
      
    currency = models.CharField(
        max_length=3,
        choices=CURRENCY_CHOICES,
        default='INR')
      
    price = models.IntegerField()
      
    manufacturing_date = models.DateTimeField()
      
    owner = models.ForeignKey(
        'auth.User',
        related_name='robots',
        on_delete=models.CASCADE
    )
  
    class Meta:
        ordering = ('name',)
  
    def __str__(self):
        return self.name

En el código anterior, especificamos el valor models.CASCADE para que cada vez que eliminemos un usuario, los robots asociados con este usuario también se eliminarán. 

Ahora agreguemos el campo de propietario a la clase RobotSerializer mencionada en el archivo robots/serializers.py. Puede agregar el siguiente código

owner = serializers.ReadOnlyField(source='owner.username')

La clase RobotSerializer tiene el siguiente aspecto:

Python3

class RobotSerializer(serializers.HyperlinkedModelSerializer):
    
    robot_category = serializers.SlugRelatedField(
        queryset=RobotCategory.objects.all(), slug_field='name')
      
    manufacturer = serializers.SlugRelatedField(
        queryset=Manufacturer.objects.all(), slug_field='name')
      
    currency = serializers.ChoiceField(
        choices=Robot.CURRENCY_CHOICES)
      
    currency_name = serializers.CharField(
        source='get_currency_display',
        read_only=True)
  
    # Display the owner's username (read-only)
    owner = serializers.ReadOnlyField(source='owner.username')
  
    class Meta:
        model = Robot
        fields = '__all__'

Vamos a crear dos nuevas clases de serializador llamadas clase UserRobotSerializer y clase UserSerializer. Puede agregar el código mencionado a continuación:

Python3

class UserRobotSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Robot
        fields = (
            'url',
            'name')
  
class UserSerializer(serializers.HyperlinkedModelSerializer):
    robots = UserRobotSerializer(
        many=True,
        read_only=True)
  
    class Meta:
        model = User
        fields = (
            'url',
            'pk',
            'username',
            'robots')

La clase UserRobotSerializer serializa los drones relacionados con un usuario. Aquí no hemos usado RobotSerializer porque solo necesitamos serializar menos campos. La clase UserSerializer declara un atributo ‘robots’ como una instancia de la clase UserRobotSerializer.

A continuación, debemos guardar información sobre los usuarios que realizan requests. Para lograr esto, debemos anular el método perform_create en la clase RobotList declarada en el archivo views.py . La nueva clase RobotList tiene el siguiente aspecto 

Python3

class RobotList(generics.ListCreateAPIView):
    
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'
  
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

El método perform_create pasa la información del propietario al método create utilizando el método serializer.save.

Aquí, hemos agregado un nuevo campo de propietario a la tabla de robots. Puede ejecutar las migraciones para reflejar los cambios en la base de datos. Recuerde, debemos asignar un propietario predeterminado a nuestros robots existentes en la tabla. Anotemos la identificación de un usuario existente y proporcionémosla durante el proceso de migración. Puede obtener la identificación usando el shell de Django. Compartiendo la captura de pantalla como referencia:

Ahora hagamos el proceso de migración. Aquí, Django mostrará el siguiente mensaje:

Ahora ejecute el comando » python manage.py migrate » para aplicar las migraciones generadas.

Configuración de políticas de permisos

Puede mencionar la clase BasicAuthentication en el archivo settings.py. 

REST_FRAMEWORK = {

    'DEFAULT_AUTHENTICATION_CLASSES':(
        'rest_framework.authentication.BasicAuthentication',

        )
}

Ahora, configuremos las políticas de permisos para las vistas basadas en clases de RobotList y RobotDetail. Debe importar los permisos y el permiso personalizado.

from rest_framework import permissions
from robots import custompermission

El nuevo código es el siguiente:

Python3

class RobotList(generics.ListCreateAPIView):
    permission_classes = (
        permissions.IsAuthenticatedOrReadOnly,
        custompermission.IsCurrentUserOwnerOrReadOnly,
    )
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'
  
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
  
  
class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (
        permissions.IsAuthenticatedOrReadOnly,
        custompermission.IsCurrentUserOwnerOrReadOnly,
    )
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'

Realización de requests HTTP

Intentemos obtener los detalles del robot. Dado que es un método seguro, nuestro permiso personalizado proporcionará los detalles del robot sin ninguna credencial de usuario. El comando HTTPie es el siguiente:

http:8000/robot/

La salida es la siguiente:

HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Language: en
Content-Length: 2116
Content-Type: application/json
Date: Sun, 29 Aug 2021 07:11:39 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Accept-Language
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "currency": "USD",
        "currency_name": "US Dollar",
        "manufacturer": "Fanuc",
        "manufacturing_date": "2019-10-12T00:00:00Z",
        "name": "FANUC M-710ic/50",
        "owner": "sonu",
        "price": 37000,
        "robot_category": "Articulated Robots",
        "url": "http://localhost:8000/robot/1/"
    },
    {
        "currency": "USD",
        "currency_name": "US Dollar",
        "manufacturer": "ABB",
        "manufacturing_date": "2020-05-10T00:00:00Z",
        "name": "IRB 1100",
        "owner": "sonu",
        "price": 25000,
        "robot_category": "Articulated Robots",
        "url": "http://localhost:8000/robot/7/"
    },
]

Ahora intentemos eliminar un robot. De acuerdo con la clase de permiso personalizada, solo el propietario del robot puede realizar la operación de eliminación. Intentemos eliminar el robot proporcionando la credencial de superusuario. El comando HTTPie es el siguiente:

http -a “admin”:”admin@123″ ELIMINAR :8000/robot/1/

Producción:

Probemos la operación de eliminación proporcionando las credenciales del propietario. El comando es el siguiente:

http -a “sonu”:”sn@pswrd” ELIMINAR :8000/robot/1/

Producción:

Puede notar que el robot se ha eliminado con éxito de la base de datos.

Publicación traducida automáticamente

Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *