Фильтрация объекта предварительной выборки с использованием OuterRef и SubQuery - PullRequest
0 голосов
/ 29 января 2020

Фон

Допустим, у меня есть такая структура:

models.py

class Location(models.Model):
    pass

class Service(models.Model):
    location = models.ForeignKey(Location, related_name="services",on_delete=models.CASCADE)

class Staff(models.Model):
    service = models.ForeignKey(Service, related_name="staffs",on_delete=models.CASCADE)

class Performance(models.Model):
    content = models.CharField("content", max_lenght=50)
    staff=models.ForeignKey(Staff, related_name="performances",on_delete=models.CASCADE)

    @property
    def location(self):
       return self.staff.service.location

Я хочу предоставить свои данные через API отдыха.

serializers.py


class StaffSerializer(serializers.ModelSerializer):
    class Meta:
        model = Staff
        fields = ['id', 'service']


class ServiceSerializer(serializers.ModelSerializer):
    staffs = StaffSerializer(many=True)
    class Meta:
        model = Service
        fields = ['id', 'staffs']


class LocationSerializer(serializers.ModelSerializer):
    services = ServiceSerializer(many=True)
    class Meta:
        model = Location
        fields = ['id',]

Структура данных

Итак ... сериализованный Location экземпляр будет выглядеть следующим образом:

{
    "id":"some_location_id",
    "services":[
        {
            "id":"some_service",
            "staffs":[
                {"id":"some_staff_id"},
                {"id":"some_other_staff_id"}
            ]
        } // ... Repeated for eah services
    ]

}

Теперь для по какой-то причине мне нужно раскрыть Performance данные по схеме, которая выглядит следующим образом:

{

    "id":"some_perfomance_id",
    "content":"Foo",
    "location":{
        "id":"performance_staff__service__location_id",
        "services":[
            {
                "id":"performance_staff__service_id",
                "staffs":[
                    {"id":"performance_staff_id"} // ... Only one staff!
                ]
            } // ... Only one service!
        ]
    }
}

Исследуемые решения

Мне удалось заставить эту работу настраивать serializers и передавать данные через некоторые личные атрибуты, но я ожидаю действительно странного поведения.

Мне нужен способ сделать это, используя Prefetch

Manager.py

Сосредоточив внимание только на services, я бы ожидал что-то вроде этого:

class PerformanceManager(models.Manager):
    def get_queryset(self):
        service_qs = SubQuery(
                        Service.objects.filter(staffs=OuteRef("staff_id")).only("pk")
                    )

        return super().get_queryset().prefetch_related(
            Prefetch(
                'staff__service__location__services',
                queryset = Services.objects.filter(pk__in=service_qs)
            )
        )

Похоже, Prefetch, SubQuery и OuterRef не очень хорошо играют вместе, и это делает смысл ... Для одного location экземпляра я хочу настроить несколько предварительно выбранных services, в зависимости от того, к какому performance я обращаюсь к местоположению ...

Опросить Ион

Есть ли какой-нибудь pythonic способ добиться этого?

...