如何在 Django 中拥有基于权限的用户系统?

ves*_*sii 5 python django django-rest-framework

我想构建一个 REST API,用户可以根据其权限对对象进行操作。考虑代表一辆汽车的记录 - 它包含许可证号、汽车类型和额外信息。还要考虑以下用户系统:

  • 所有者 - 谁拥有该car对象。可以修改也可以删除。
  • 编辑者 - 只能修改对象属性的人。
  • 查看者 - 只能查看对象属性。

每条记录可以包含多个所有者/编辑者/查看者(创建对象的用户应自动成为所有者)。此外,所有者可以添加或删除编辑者/查看者。在我的脑海中,我将其视为所有者/编辑/观众的列表。

因此,在 GET 请求的情况下,我希望能够返回用户有权访问的所有对象,分为这三个类别。

所以在我的api应用程序下,我有以下代码:

models.py文件包含:

class CarRecord(models.Model):
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

serializers.py文件包含:

class CarRecordSerializer(serializers.ModelSerializer):
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)

    class Meta:
        model = CarRecord
        fields = ('__all__')
Run Code Online (Sandbox Code Playgroud)

view.py我有:

class CarRecordViews(APIView):
    def get(self, request):
        if not request.user.is_authenticated:
            user = authenticate(username=request.data.username, password=request.data.password)
             if user is not None:
                return Response(data={"error": "invalid username/password"}, status=status.HTTP_401_UNAUTHORIZED)
    # return all records of cars that user some type of permission for
            
Run Code Online (Sandbox Code Playgroud)

现在,我想获取user他有权查询的所有记录(及其权限类型)。我想在下面添加三个额外的字段CarRecord- 每个字段都是包含该权限类型的用户列表。但我不确定这是否是“Django 方式”。所以想先咨询一下SO。

编辑:我尝试将以下字段添加到我的CarRecord班级中:

owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[])
Run Code Online (Sandbox Code Playgroud)

我还补充道:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username']

lass CarRecordSerializer(serializers.ModelSerializer):
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)
    owners = UserSerializer(many=True)

    class Meta:
        model = CarRecord
        fields = ('__all__')
Run Code Online (Sandbox Code Playgroud)

我创建实例的方式CarRecordSerializer是:

serializer = CarRecordSerializer(data=request.data)
Run Code Online (Sandbox Code Playgroud)

但我得到:

{
    "error": {
        "owners": [
            "This field is required."
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

如何让它发挥作用?我想我的问题是如何序列化 ManyToMany 对象?

EDIT2:我的第二次尝试是:

class CarRecord(models.Model):
    date_created = models.DateTimeField()
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)
    owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[]))
    creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)

# ...

class CarRecordSerializer(serializers.ModelSerializer):
    date_created = serializers.DateTimeField(default=datetime.now(timezone.utc))
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)
    creator = serializers.StringRelatedField()
    owners = serializers.PrimaryKeyRelatedField(many=True,read_only=True)

    class Meta:
        model = CarRecord
        fields = '__all__'

    def create(self, validated_data):
        self.owners = [self.context['creator']]
        record = CarRecord(**validated_data, creator=self.context['creator'])
        record.save()
        return record

# ... 

# In post method:
serializer = CarRecordSerializer(data=request.data, context={ 'creator': user })
Run Code Online (Sandbox Code Playgroud)

但现在,在 GET 方法中,我使用用户过滤所有者列表,但找不到对象:

> CarRecord.objects.filter(owners=user)
<QuerySet []>
Run Code Online (Sandbox Code Playgroud)

另外,在“管理”部分中,我看到所有对象自动将所有用户包含在列表中owners/editors/viewers。这是为什么?所有者应仅包含创建记录的用户,编辑者和查看者应为空列表。在另一个查询中,所有者可以添加其他所有者/编辑者/查看者。

小智 1

这是我认为可能是正确的解决方案

class CarRecord(models.Model):
    date_created = models.DateTimeField(auto_now_add=True)
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)
    owners = models.ManyToManyField(User, related_name='car_owners')
    creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)

class CarRecordSerializer(serializers.ModelSerializer):
    creator = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
    owners_details = UserSerializer(source='owners', many=True, read_only=True)

    class Meta:
        model = CarRecord
        fields = '__all__'
    
    def create(self, validated_data):
        try:
            new_owners = validated_data.pop('owners')
        except:
            new_owners = None
        car_record = super().create(validated_data)
        if new_owners:
            for new_owner in new_owners:
                car_record.owners.add(new_owner)
        return car_record
Run Code Online (Sandbox Code Playgroud)

在views.py中

from rest_frameword import generics
from rest_framework import permissions

class CustomCarRecordPermissions(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method == 'GET':
            return True
        elif request.method == 'PUT' or request.method == 'PATCH':
            return request.user == obj.creator or request.user in obj.owners.all()
        elif request.method == 'DELETE':
            return request.user == obj.creator
        return False

 class CarRecordListCreate(generics.ListCreateAPIView):
     permission_classes = (IsAuthenticated, )
     serializer_class = CarRecordSerializer
     queryset = CarRecord.objects.all()
     
     def post(self, request, *args, **kwargs):
         request.data['creator'] = request.user.id
         return super().create(request, *args, **kwargs)

class CarRecordDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (CustomCarRecordPermissions, )
    serializer_class = CarRecordSerializer
    lookup_field = 'pk'
    queryset = CarRecord.objects.all()
    
    
Run Code Online (Sandbox Code Playgroud)

模型是不言自明的;在CarRecord序列化器中,我们将创建者设置为必需的False和主键相关字段,以便我们可以在创建之前提供请求用户ID,如views.py post方法中所示。

在详细视图中,我们设置自定义权限;如果请求是 GET 我们允许权限。但如果请求是 PUT 或 PATCH,则允许所有者和创建者。但如果是删除请求,则仅允许创建者。