Django ORM методы фильтрации запросов запуск множественного фильтра дублирует джойнты

Я пытаюсь запустить фильтры, использующие методы в двух отдельных атрибутах.

В ICD10Filter:

class Icd10Filter(filters.FilterSet):


    # New Filters for DOS Range
    dosFrom = filters.DateFilter(method='filter_by_dos_from', lookup_expr='gte')
    dosTo = filters.DateFilter(method='filter_by_dos_to', lookup_expr='lte')



    def filter_by_dos_from(self, queryset, name, value):
    
        return queryset.filter(
            base_icd__chart_review_dos__dos_from__gte=value
        )

    def filter_by_dos_to(self, queryset, name, value):        
        return queryset.filter(
            base_icd__chart_review_dos__dos_to__lte=value
        )

Фильтр ICD10 упоминается в модели ChartReviewDx:

class ChartReviewDx(models.Model):
    chart_review_dos = models.ForeignKey(
        ChartReviewDos, on_delete=models.SET_NULL, null=True, related_name="diagnosis_details"
    )
    diagnosis_code = models.CharField(max_length=1024, null=True, blank=True)
    diagnosis_description = models.CharField(max_length=1024, null=True, blank=True)
    icd10 = models.ForeignKey("risk_adjustment.Icd10", on_delete=models.SET_NULL, null=True)
    base_icd = models.ForeignKey(
        "risk_adjustment.Icd10", on_delete=models.SET_NULL, null=True, blank=True, related_name="base_icd"
    )

и ChartReviewDx ссылается на модель ChartReviewDOS:

class ChartReviewDos(models.Model):
    chart = models.ForeignKey(Chart, on_delete=models.SET_NULL, null=True, blank=True, related_name="diagnosis")
    dos_from = models.DateField()
    dos_to = models.DateField()

Я хочу получить коды МКБ10 только для определенного диапазона DOS.

Желаемый запрос:

SELECT 
  distinct id, 
  code, 
  description 
FROM 
  risk_adjustment_icd10 
  INNER JOIN healthcare_data_chart_review_dx ON (
    id = healthcare_data_chart_review_dx.base_icd_id
  ) 
  INNER JOIN healthcare_data_chart_review_dos ON (
    healthcare_data_chart_review_dx.chart_review_dos_id = healthcare_data_chart_review_dos.id
  ) 
WHERE 
  (
    valid = 1     
    AND healthcare_data_chart_review_dos.dos_from >= '2023-08-19' 
    AND healthcare_data_chart_review_dos.dos_to <= '2023-08-19' 
  ) 
ORDER BY 
  code ASC

Когда я запускаю фильтр только для одного из полей, запрос работает нормально.

Но запуск фильтров по обоим полям дает избыточные JOINS и, следовательно, неточные результаты:

Запрос, который формируется после применения обоих фильтров:

SELECT 
  DISTINCT id, 
  code, 
  description 
FROM 
  risk_adjustment_icd10 
  INNER JOIN healthcare_data_chart_review_dx ON (
    id = healthcare_data_chart_review_dx.base_icd_id
  ) 
  INNER JOIN healthcare_data_chart_review_dos ON (
    healthcare_data_chart_review_dx.chart_review_dos_id = healthcare_data_chart_review_dos.id
  ) 
  INNER JOIN healthcare_data_chart_review_dx T4 ON (
    id = T4.base_icd_id
  ) 
  INNER JOIN healthcare_data_chart_review_dos T5 ON (
    T4.chart_review_dos_id = T5.id
  ) 
WHERE 
  (
    valid = 1 
    AND healthcare_data_chart_review_dos.dos_from >= '2023-08-19' 
    AND T5.dos_to <= '2023-08-19' 
  ) 
ORDER BY 
  code asc

Как удалить эти лишние соединения?

Если вы присоединяетесь к моделям, есть огромная разница между:

queryset.filter(
    base_icd__chart_review_dos__dos_from__gte=value
).filter(
    base_icd__chart_review_dos__dos_to__lte=value
)

и:

queryset.filter(
    base_icd__chart_review_dos__dos_from__gte=value,
    base_icd__chart_review_dos__dos_to__lte=value,
)

В форме запрашивается запись, в которой есть связанная review_dos, для которой from больше или равно value, и связанная review_dos, , возможно, другая , для которой to меньше или равно value. Для последнего эта review_dos запись должна быть одной и той же.

Если вы используете same .filter(…) [Django-doc], то JOINы будут повторно использоваться в пунктах этого .filter(…), тогда как отдельные вызовы .filter(…) будут генерировать разные джойны.

Таким образом, это не ошибка, оба случая допустимы, в зависимости от того, что вы хотите, вы используете один из сценариев.

Таким образом, вам, вероятно, следует сделать третий метод фильтрации, в котором вы используете тот же .filter(…).

Вернуться на верх