Django REST Framework 中如何处理相关字段的预选并确保多对多关系的正确更新?

问题描述 投票:0回答:1

我正在致力于使用 Django 和 Django Rest Framework 实现基于角色的访问控制。我想通过 DRF 可浏览 API 创建一个具有一组权限的角色。此外,我需要更新这些权限的功能,包括添加新权限和删除现有权限。在可浏览的 API 中显示角色时,为了清晰起见,我希望预先选择关联的权限,同时还显示所有其他可用的权限以便于添加。

到目前为止我做了什么

这是我的模型的简化版本

class BaseModel(models.Model):
    pkid = models.BigAutoField(primary_key=True, editable=False)
    id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)

    class Meta:
        abstract = True

class Role(BaseModel):
    name = models.CharField(max_length=100)

class Permission(BaseModel):
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField(null=True, blank=True)

class RolePermission(BaseModel):
    role = models.ForeignKey(
        Role, on_delete=models.CASCADE, related_name="role_permissions"
    )
    permission = models.ForeignKey(Permission, on_delete=models.CASCADE)

这是我的序列化器

class RoleSerializer(serializers.ModelSerializer):
    permissions = serializers.SlugRelatedField(
        queryset=Permission.objects.all(), many=True, required=False, slug_field="name"
    )
    business = serializers.PrimaryKeyRelatedField(
        queryset=Business.objects.all(), required=False, allow_null=True
    )

    class Meta:
        model = Role
        fields = ("id", "name", "is_default", "permissions", "business")

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        permissions = instance.role_permissions.all().values_list(
            "permission__name", flat=True
        )
        representation["permissions"] = list(permissions)
        return representation

    def to_internal_value(self, data):
        data = data.copy()
        permissions_data = data.pop("permissions", [])
        permissions_qs = Permission.objects.filter(name__in=permissions_data)
        data["permissions"] = [permission.id for permission in permissions_qs]
        return super().to_internal_value(data)

预选有效,但是当我发送更新请求来修改权限时,我收到如下错误:

{
    "permissions": [
        "Object with name=[UUID('e4ac49c0-093a-4281-96b0-0846917fc62b')] does not exist."
    ]
}

我通过将序列化器重写为类似的内容来修复错误

class RoleSerializer(serializers.ModelSerializer):
    permissions = serializers.PrimaryKeyRelatedField(
        queryset=Permission.objects.all(), many=True, required=False
    )
    business = serializers.PrimaryKeyRelatedField(
        queryset=Business.objects.all(), required=False, allow_null=True
    )

    class Meta:
        model = Role
        fields = ("id", "name", "is_default", "permissions", "business")

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        permissions = instance.role_permissions.all().values_list(
            "permission", flat=True
        )
        representation["permissions"] = list(permissions)
        return representation

    def to_internal_value(self, data):
        data = data.copy()
        permissions_data = data.pop("permissions", [])
        internal_value = super().to_internal_value(data)
        internal_value["permissions"] = permissions_data
        return internal_value

但是这样做会影响返回的 json,现在我得到的是这样的 ID,而不是权限名称

{
    "id": "0b3de100-82ef-4a02-b94e-659cb6dd4314",
    "name": "neweee",
    "is_default": false,
    "business": null,
    "permissions": [
        1,
        2
    ]
}

我的问题: 如何正确地预先选择相关字段(权限)作为 DRF 可浏览 API 中的名称,同时仍然能够无错误地创建/更新?

谢谢你

python django django-rest-framework
1个回答
0
投票
正如文档所述,

PrimaryKeyRelatedField
可以使用其主键表示关系的目标。 这里

SlugRelatedField
可用于使用目标上的字段来表示关系的目标。 这里

例如:

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
     )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

输出:

{
    'album_name': 'Dear John',
    'artist': 'Loney Dear',
    'tracks': [
        'Airport Surroundings',
        'Everything Turns to You',
        'I Was Only Going Out',
        ...
    ]
}

个人解决方案:

class PermissionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Permission
        fields = ['id', 'name'] 

在您的

RoleSerializer

中使用此序列化器
class RoleSerializer(serializers.ModelSerializer):
    permissions = PermissionSerializer(many=True, required=False)
    # remaining code here .....
© www.soinside.com 2019 - 2024. All rights reserved.