Как получить четкие результаты при заказе по связанному аннотированному полю? - PullRequest
1 голос
/ 24 февраля 2020

Это проект Django (2.2) с использованием Python (3.7). Учитывая следующие модели, как я могу получить отличные результаты в запросе ниже?

class Profile(models.Model):
    user = models.ForeignKey(User, ...)


class Location(models.Model):
    profile = models.ForeignKey(Profile, ...)
    point = PointField()


class ProfileService(models.Model):
    profile = models.ForeignKey(Profile, ...)
    service = models.ForeignKey(Service, ...)

Вот мой запрос, который работает до сих пор, но я получаю дубликаты объектов ProfileService:

service = Service.objects.get(id=1)

qs = (
  ProfileService.objects
  .filter(service=service)
  .annotate(distance=Distance('profile__location__point', self.point))
  .order_by('distance')
)

Если я добавлю .distinct('profile'), то, очевидно, произойдет сбой с SELECT DISTINCT ON expressions must match initial ORDER BY expressions.

У меня такое ощущение, что решение заключается в использовании __in, но мне нужно оставить аннотированное поле distance.

Дальнейшее объяснение

Чтобы проиллюстрировать далее, приведенные ниже списки представляют собой фиктивные данные, которые будут воспроизводить проблему:

services = [
    { 'id': 1, 'service': 'A', ... },
    { 'id': 2, 'service': 'B', ... },
]

users = [
    { 'id': 1, 'username': 'Jane Doe', 'email': 'jane@test.com', ... },
    { 'id': 2, 'username': 'John Doe', 'email': 'john@test.com', ... },
]

profiles = [
    { 'id': 1, 'user': 1, ... },
    { 'id': 2, 'user': 2, ... },
]

locations = [
    { 'id': 1, 'profile': 1, 'point': 'X', ... },
    { 'id': 2, 'profile': 1, 'point': 'Y', ... },
    { 'id': 3, 'profile': 2, 'point': 'Z', ... },
]
# 'point' would normally contain actual Point data.
# Letters (XYZ) just intended to represent unique Point data.

profile_services = [
    { 'id': 1, 'profile': 1, 'service': 1 },
    { 'id': 2, 'profile': 1, 'service': 2 },
    { 'id': 3, 'profile': 2, 'service': 1 },
]

Это объекты 'Location', которые вызывают дублирование в приведенном выше наборе запросов «qs» (если с «Профилем» связано только 1 «Местоположение», в «qs» нет повторяющегося результата), однако пользователю необходимо сохранить возможность предоставления нескольких местоположений, мы просто нужен ближайший.

Прогресс

Следуя совету Ивана Старостина, я собрал следующее с помощью подзапросов:

locations = (
    Location.objects
    .filter(profile=OuterRef('profile'))
    .annotate(distance=Distance('point', self.point))
    .order_by('distance')
)

qs = (
    ProfileService.objects
    .filter(service=service)
    .filter(profile__id__in=Subquery(locations.values('profile_id')[:1]))
    .annotate(distance=Subquery(locations.values('distance')[:1]))
)

Теперь это решает проблему дублирует результаты, но теряет аннотированное значение «расстояние», которое следует аннотировать в отношении соответствующего объекта запроса ProfileService. Не уверенный, идет ли это в правильном направлении или нет (любые указатели были бы очень благодарны), я просто хочу избежать вытягивания данных в Python память, чтобы избавиться от дубликатов.

Я был ссылаясь на следующий пост, но принятый ответ отказывается работать в моем наборе запросов: Аналогичный вопрос

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...