Отбрасывание дубликатов при запросе нескольких таблиц в Django
У меня есть пользовательский менеджер с поиском, который упорядочивает возвращаемые результаты по рангу:
class MyManager(models.Manager):
def search(self, query, fa, fb=None, fc=None, fd=None, qs=None):
if not qs:
qs = self.get_queryset()
try:
if not (1 in [c in query for c in '&|()!*:"']):
query = " & ".join([f"{q}:*" for q in query.split()])
vector = SearchVector(*fa, weight="A", config="english")
if fb:
vector += SearchVector(*fb, weight="B", config="english")
if fc:
vector += SearchVector(*fc, weight="C", config="english")
if fd:
vector += SearchVector(*fd, weight="D", config="english")
query = SearchQuery(query, search_type="raw")
qs = (
qs.annotate(search=vector, rank=SearchRank(vector, query))
.filter(search=query)
.order_by("-rank", "-id")
.distinct("rank", "id")
)
qs.count() # Trigger exception
except (ProgrammingError, UnboundLocalError):
qs = qs.none()
return qs
Но когда я пытаюсь выполнить поиск по связанным полям, он все равно возвращает дублирующиеся результаты:
class Case(models.Model):
machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
user = models.ForeignKey(Profile, on_delete=models.SET_NULL)
hashtags = models.CharField(max_length=255)
closed = models.BooleanField(default=False)
objects = MyManager()
class CaseProgress(models.Model):
case = models.ForeignKey(Case, on_delete=models.CASCADE)
desc = models.TextField()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
class CaseListView(ListView):
model = Case
def get_queryset(self):
query = self.request.GET.get("query", None)
show_closed = (
True if self.request.GET.get("show_closed", False) == "true" else False
)
if query:
if not show_closed:
qs = self.model.objects.filter(closed=False)
else:
qs = self.model.objects.all()
fa = (
"id",
"machine__serial_number",
"machine__company__name",
"user__user__first_name",
"user__user__last_name",
)
fb = ("hashtags",)
fc = ("caseprogress__desc",)
qs = self.model.objects.search(
query, fa, fb, fc, qs=qs
)
return qs
Теперь я запускаю поиск, и запрос может быть найден в разных таблицах, чем возвращает оба результата, даже если у меня стоит distinct("id")
. Почему так? Я читал руководство Django на эту тему, но, похоже, оно не применимо в моем случае, так как я не упорядочиваю по ралированным полям.
Ваш запрос указывает .distinct("rank", "id")
, который не идентичен .distinct("id")
.
В первом случае будут получены результаты, соответствующие обоим полям, а не одному или другому. То есть строки с одинаковыми id
будут появляться в наборе запросов до тех пор, пока в них не будет одинаковых rank
.
РЕДАКТИРОВАТЬ в ответ на комментарий:
В документации говорится следующее о distinct
при использовании Postgres
:
При указании имен полей необходимо указать
order_by()
в QuerySet, и поля вorder_by()
должны начинаться с полей вdistinct()
, в том же порядке.
Другими словами, любое поле, перечисленное в distinct
, должно быть также перечислено в order_by
. Но обратное не обязательно - вы можете перечислить поля в order_by
, не перечисляя их в distinct
(при желании вы можете полностью опустить distinct
, и order_by
все равно будет работать).
В документации приведен конкретный пример, демонстрирующий это:
# Examples (those after the first will only work on PostgreSQL):
Entry.objects.order_by("author", "pub_date").distinct("author")