我想要构建一个REST,用户可以根据对象的权限对其进行操作。考虑一条表示汽车的记录--它包含车牌号码、汽车类型和额外信息。还请考虑以下用户系统:
car对象。可以修改和删除它。每个记录可以包含多个所有者/编辑器/查看器(创建对象的用户应该自动成为所有者)。此外,所有者还可以添加或删除编辑器/查看器。在我看来,我把它看作是一份所有者/编辑/观众名单。
因此,在GET请求的情况下,我希望能够返回用户拥有权限的所有对象,并将其分为这三个类别。
因此,在我的api应用程序中,我有以下代码:
models.py文件包含:
class CarRecord(models.Model):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)serializers.py文件包含:
class CarRecordSerializer(serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
class Meta:
model = CarRecord
fields = ('__all__')在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现在,我想获得他有权限查询的所有user记录(以及权限类型)。我想在CarRecord下添加三个额外的字段--每个字段都是包含该权限类型的用户列表。但我不确定这是不是"Django方式“。所以想先跟他商量一下。
编辑:我尝试将以下字段添加到我的CarRecord类中:
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[])我还补充说:
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实例的方式是:
serializer = CarRecordSerializer(data=request.data)但我明白:
{
"error": {
"owners": [
"This field is required."
]
}
}怎么让它起作用?我想我的问题是如何序列化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 })但是现在,在GET方法中,我用用户过滤所有者列表,它找不到对象:
> CarRecord.objects.filter(owners=user)
<QuerySet []>另外,在Admin部分,我看到所有对象都自动拥有owners/editors/viewers列表中的所有用户。为什么会这样呢?所有者应该只包含创建记录的用户,编辑器和查看者应该是空列表。在另一个查询中,所有者可以添加其他所有者/编辑器/查看器。
发布于 2022-06-23 05:45:37
以下是我可能认为是正确的解决办法
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中
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,则允许权限。但是,如果请求被放置或修补,所有者和创建者将被允许。但如果是删除请求,则只允许创建者。
发布于 2022-06-23 14:46:05
我认为django-rest-framework-guardian包适合这里。这个包基于django-guardian。
Django -守护者是Django对象权限的实现,提供了额外的身份验证后端。
你的models.py没有变化
您应该更改serializers.py和views.py。例如,序列化程序应该如下所示
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]
}你的观点应该是这样的:
from rest_framework_guardian import filters
class CarRecordModelViewSet(ModelViewSet):
queryset = CarRecord.objects.all()
serializer_class = CarRecordSerializer
filter_backends = [filters.ObjectPermissionsFilter]像这样编辑settings.py:
INSTALLED_APPS = [
'rest_framework',
'guardian',
]
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"guardian.backends.ObjectPermissionBackend",
]您也可以在设置中全局定义筛选后端:
REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework_guardian.filters.ObjectPermissionsFilter",
],
}别忘了!如果在ObjectPermissionsFilter中定义settings.py,则所有视图都会受到此筛选器的影响。
如果要限制每个用户的post请求,则应该实现自定义权限类,如下所示:
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的详细信息
发布于 2022-06-26 12:23:58
您可以编写权限类的车主用户。
你的模特。
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
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此权限类将检查用户是否存在,用户是否经过身份验证,以及用户是否属于卡记录。
你可以在你的视野中通过这个许可。
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
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
),
} https://stackoverflow.com/questions/72689334
复制相似问题