首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Django中拥有基于权限的用户系统?

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

Stack Overflow用户
提问于 2022-06-20 15:17:41
回答 3查看 277关注 0票数 5

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

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

每个记录可以包含多个所有者/编辑器/查看器(创建对象的用户应该自动成为所有者)。此外,所有者还可以添加或删除编辑器/查看器。在我看来,我把它看作是一份所有者/编辑/观众名单。

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

因此,在我的api应用程序中,我有以下代码:

models.py文件包含:

代码语言:javascript
复制
class CarRecord(models.Model):
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)

serializers.py文件包含:

代码语言:javascript
复制
class CarRecordSerializer(serializers.ModelSerializer):
    type = models.CharField(max_length=50)
    license = models.CharField(max_length=50)

    class Meta:
        model = CarRecord
        fields = ('__all__')

view.py中,我有:

代码语言:javascript
复制
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

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

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

代码语言:javascript
复制
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[])

我还补充说:

代码语言:javascript
复制
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__')

我创建CarRecordSerializer实例的方式是:

代码语言:javascript
复制
serializer = CarRecordSerializer(data=request.data)

但我明白:

代码语言:javascript
复制
{
    "error": {
        "owners": [
            "This field is required."
        ]
    }
}

怎么让它起作用?我想我的问题是如何序列化ManyToMany对象?

EDIT2:我的第二个尝试是:

代码语言:javascript
复制
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 })

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

代码语言:javascript
复制
> CarRecord.objects.filter(owners=user)
<QuerySet []>

另外,在Admin部分,我看到所有对象都自动拥有owners/editors/viewers列表中的所有用户。为什么会这样呢?所有者应该只包含创建记录的用户,编辑器和查看者应该是空列表。在另一个查询中,所有者可以添加其他所有者/编辑器/查看器。

EN

回答 3

Stack Overflow用户

发布于 2022-06-23 05:45:37

以下是我可能认为是正确的解决办法

代码语言:javascript
复制
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

在views.py中

代码语言:javascript
复制
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()

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

在详细视图中,我们设置了自定义权限;如果请求是GET,则允许权限。但是,如果请求被放置或修补,所有者和创建者将被允许。但如果是删除请求,则只允许创建者。

票数 1
EN

Stack Overflow用户

发布于 2022-06-23 14:46:05

我认为django-rest-framework-guardian包适合这里。这个包基于django-guardian

Django -守护者是Django对象权限的实现,提供了额外的身份验证后端。

你的models.py没有变化

您应该更改serializers.pyviews.py。例如,序列化程序应该如下所示

代码语言:javascript
复制
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin    

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

    class Meta:
        model = CarRecord
        fields = ('__all__')

    def get_permissions_map(self, created):
        current_user = self.context['request'].user
        readers = Group.objects.get(name='readers')
        editors = Group.objects.get(name='editors')
        owners = Group.objects.get(name='owners')

        return {
          'view_car_record': [current_user, readers, owners],
          'change_car_record': [current_user, editors],
          'delete_car_record': [current_user, owners]
        }

你的观点应该是这样的:

代码语言:javascript
复制
from rest_framework_guardian import filters

class CarRecordModelViewSet(ModelViewSet):
    queryset = CarRecord.objects.all()
    serializer_class = CarRecordSerializer
    filter_backends = [filters.ObjectPermissionsFilter]

像这样编辑settings.py

代码语言:javascript
复制
INSTALLED_APPS = [
    'rest_framework',
    'guardian',
]

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "guardian.backends.ObjectPermissionBackend",
]

您也可以在设置中全局定义筛选后端:

代码语言:javascript
复制
REST_FRAMEWORK = {    
    "DEFAULT_FILTER_BACKENDS": [
        "django_filters.rest_framework.DjangoFilterBackend",
        "rest_framework_guardian.filters.ObjectPermissionsFilter",
    ],
}

别忘了!如果在ObjectPermissionsFilter中定义settings.py,则所有视图都会受到此筛选器的影响。

如果要限制每个用户的post请求,则应该实现自定义权限类,如下所示:

代码语言:javascript
复制
from rest_framework import permissions


class CustomObjectPermissions(permissions.DjangoObjectPermissions):
    """
    Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
    """
    perms_map = {
        'GET': ['%(app_label)s.view_%(model_name)s'],
        'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
        'HEAD': ['%(app_label)s.view_%(model_name)s'],
        'POST': ['%(app_label)s.add_%(model_name)s'],
        'PUT': ['%(app_label)s.change_%(model_name)s'],
        'PATCH': ['%(app_label)s.change_%(model_name)s'],
        'DELETE': ['%(app_label)s.delete_%(model_name)s'],
    }

检查此链接以获取CustomObjectPermissions的详细信息

票数 0
EN

Stack Overflow用户

发布于 2022-06-26 12:23:58

您可以编写权限类的车主用户。

你的模特。

代码语言:javascript
复制
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)

权限类permission.py

代码语言:javascript
复制
from rest_framework.permissions import BasePermission,
from cars.models import CarRecord

class isCarAccess(BaseCommand):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        check_user = CarRecord.objects.filter(owners__in=[request.user])
        return request.user is not None and request.user.is_authenticated and check_user

此权限类将检查用户是否存在,用户是否经过身份验证,以及用户是否属于卡记录。

你可以在你的视野中通过这个许可。

代码语言:javascript
复制
from .permission import  isCarAccess
from .models import  CarRecord
class CarRecordViews(APIView):

    permission_classes = [isCarAccess]

    def get(self, request):
         car_record = CarRecord.objects.filter(owners__in=[request.user])
         # return all records of cars that user some type of permission for

和你的settings.py

代码语言:javascript
复制
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (  
       "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.TokenAuthentication",
    ),
}   
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72689334

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档