Отбрасывание дубликатов при запросе нескольких таблиц в 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")
Вернуться на верх