Это проект 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 память, чтобы избавиться от дубликатов.
Я был ссылаясь на следующий пост, но принятый ответ отказывается работать в моем наборе запросов: Аналогичный вопрос