如何在 Django REST API 框架中公开过滤后的反向关系?

How to expose a filtered reverse relationship in Django REST API framework?

我有两个模型:

class Task(models.Model):
    person = models.ForeignKey(Person, related_name='tasks')
    title = models.CharField(max_length=200)
    is_deleted = models.BooleanField(default=False)

class Person(models.Model):
    name = models.CharField(max_length=100)
    is_deleted = models.BooleanField(default=False)

还有两个序列化程序:

class PersonSerializer(serializers.ModelSerializer):
    tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ('id', 'name', 'tasks')

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ('id', 'title')

我想建立一个 API 其中 returns 人的详细信息以及属于此人的未删除任务列表。

如:

{
    "id": 1,
    "name": "Alan",
    "tasks": [
        100,
        101,
        102
    ]
}

所以在 view.py 中,我有:

@api_view(['GET'])
def api_person_detail(request, person_id):
    """
    Project details
    """
    if request.method == 'GET':
        try:
            person = Person.objects.get(is_deleted=False, pk=person_id)
        except Person.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = PersonSerializer(person)
        return Response(serializer.data)

除了任务 101 被删除 (is_delete=True),一切正常。我不希望它出现在回应中。在这种情况下,如何过滤所有已删除的任务?

有几种方法 -> 从更一般到更具体

通用一:将TaskManager更改为始终return Tasks with is_deleted = False.

class TaskManager(models.Manager):
    def get_queryset(self):
        return super(self.__class__, self).get_queryset().filter(is_deleted=False)


class Task(models.Model):
    # ...
    objects = TaskManager()

具体一:使用prefetch_related with custom Prefetch对象。

person = Person.objects.filter(is_deleted=False, pk=person_id) \
            .prefetch_related(
                Prefetch('task_set', queryset=Task.objects.filter(is_deleted=False))
            ) \
            .get()

当然,您可以通过将此方法移动到 PersonManager class 中来创建一个更 中级 的解决方案,这样您就可以做出如下内容:

person = Person.objects.get_person_without_deleted_tasks(pk=person_id)