自定义对象级权限 – Django REST框架

自定义对象级权限 – Django REST框架

在这篇文章中,我们将讨论如何在Django REST框架中自定义对象级权限。要在Django REST框架中定制权限类,我们应该继承rest_framework.permissions.BasePermission类,并实现以下任一或两个方法。

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

如果我们看一下Django REST框架中的Browsable API中提到的机器人模型,我们可以注意到,即使在我们的RESTFul网络服务中设置了权限策略,任何认证的用户都可以删除机器人。这里就涉及到了定制对象级权限的重要性,以便只有机器人所有者可以更新或删除现有机器人。

创建自定义权限类

进入有views.py文件的robots文件夹,创建一个名为custompermission.py的新文件。你可以在这个新文件中写下下面的代码。

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_继承自BasePermission类,并重写了has_object_permission方法。该方法返回一个bool值,表示该权限是否应该被授予。has_object_permission区分了安全和不安全的方法,只有所有者被授予不安全方法的权限。

让我们在 robots/models.py 文件中添加所有者字段。

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

机器人类看起来如下。

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

在上面的代码中,我们指定了models.CASCADE值,这样,每当我们删除一个用户,与这个用户相关的机器人也会被删除。

现在让我们把所有者字段添加到robots/serializers.py文件中提到的RobotSerializer类中。你可以添加下面的代码

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

RobotSerializer类看起来如下。

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__'

让我们创建两个新的序列化器类,名为UserRobotSerializer类和UserSerializer类。你可以添加下面提到的代码。

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')

UserRobotSerializer类将与用户相关的无人机序列化。这里我们没有使用RobotSerializer,因为我们只需要序列化较少的字段。UserSerializer类声明了一个’机器人’属性,作为UserRobotSerializer类的一个实例。

接下来,我们需要保存发出请求的用户的信息。为了达到这个目的,我们需要在views.py文件中声明的RobotList类中重写perform_create方法。新的RobotList类看起来如下

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)

perform_create方法使用serializer.save方法将所有者信息传递给创建方法。

在这里,我们在机器人表中添加了一个新的所有者字段。你可以执行迁移来反映数据库的变化。记住,我们需要给表中现有的机器人分配一个默认的所有者。让我们注意到一个现有用户的id,并在迁移过程中提供它。你可以使用Django shell获得该id。分享截图供参考。

自定义对象级权限 - Django REST框架

现在让我们来进行迁移过程。这里,Django会显示以下信息。

自定义对象级权限 - Django REST框架

现在运行 ” python manage.py migrate ” 命令来应用生成的迁移。

设置权限策略

你可以在settings.py文件中提到BasicAuthentication类。

REST_FRAMEWORK = {

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

        )
}

现在,让我们为基于RobotList和RobotDetail类视图配置权限策略。你应该导入权限和自定义权限。

from rest_framework import permissions
from robots import custompermission

新的代码如下。

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'

提出HTTP请求

让我们试着获取机器人的详细信息。由于这是一个安全的方法,我们的自定义权限将提供机器人的详细信息,而不需要任何用户凭证。HTTPie的命令如下。

http :8000/robot/

输出结果如下。

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/"
    },
]

现在让我们试着删除一个机器人。根据自定义权限类,只有机器人所有者可以执行删除操作。让我们尝试通过提供超级用户的凭证来删除机器人。HTTPie命令如下。

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

输出:

自定义对象级权限 - Django REST框架

让我们试试通过提供所有者凭证来进行删除操作。该命令如下。

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

输出:

自定义对象级权限 - Django REST框架

你可以注意到,该机器人已被成功地从数据库中删除。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程