在 django rest 框架中保存嵌套的单个对象

Saving nested single object in django rest framework

我有 3 个对象 - 公司、专家和 RecentProject,其中 RecentProject 具有公司的外键和专家的多对多关系:

class RecentProject(BaseProfileObject):
    company = models.ForeignKey(
        'experts.Company',
        verbose_name=_("Company"),
        blank=True,
        null=True,
        related_name='projects')
    experts = models.ManyToManyField(
        "Expert",
        verbose_name=_("Experts"),
        blank=True,
        null=True,
        related_name='projects')
    class Meta:
        verbose_name = _("Recent project")
        verbose_name_plural = _("Recent projects")

我想要的是能够从像

这样的端点post最近的新项目

api/v1/companies/1/recentprojects/ 和 api/v1/experts/1/recentprojects/

在第一种情况下,序列化程序应该能够验证(或者更确切地说,进行权限检查)用户是否可以对公司进行更改,在第二种情况下,用户是否可以对专家进行更改。 我似乎找不到的是如何让权限了解父对象的示例(第一种情况是公司,第二种情况是专家)。

我知道这可以通过权限来完成,所以我创建了如下权限:

class UserCanModifyCompany(permissions.BasePermission):
    def has_permission(self, request, view):
        # Do check here
        return False

然后我将它添加到我的视图集中: class最近的项目视图集(viewsets.ModelViewSet): 查询集 = RecentProject.objects.all() serializer_class = RecentProjectSerializer

@permission_classes((UserIsAuthenticatedExpert, UserCanModifyCompany))
def create(self, request, *args, **kwargs):
    return super(RecentProjectViewSet, self).create(request, *args, **kwargs)

@permission_classes((UserIsAuthenticatedExpert, UserCanModifyCompany))
def update(self, request, *args, **kwargs):
    return super(RecentProjectViewSet, self).update(request, *args, **kwargs)

但是我如何确保权限检查具有可用于 company/expert 访问检查的信息。或者我是否需要为两个 company/expert 最近保存的项目创建不同的视图集并为它们都具有不同的权限 classes?

首先,为同一资源创建两个不同的端点不是一个好主意。我建议您使用单端点:api/v1/recentprojects/。 话虽如此,我也遇到过类似情况,我认为你最好的选择是在 公司和专家视图集 上使用 @detail_route . 例如,在您的 ExpertViewSet:

@detail_route(methods=['post'], , permission_classes=[UserIsAuthenticatedExpert, UserCanModifyCompany])
    def recentprojects(self, request, pk=None):
        expert = self.get_object()
        serializer = RecentProjectSerializer(data=request.data)
        if serializer.is_valid():
            recent_project = serializer.save(company=somecompany)
            recent_project.experts.add(expert)
            return Response({'status': 'ok'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

这将使您 post 新的近期项目成为 api/v1/experts/{pk}/recentprojects/。 至于权限,你需要实现 has_object_permission 因为这是一个单一的对象端点:

class UserIsAuthenticatedExpert(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        return some_condition

您还可以覆盖 get_querysetself.action 以过滤端点可以操作的 Expert 或 Company 实例,但这会引发 404 错误而不是 403。