Django запрос без ожидаемых результатов - PullRequest
1 голос
/ 07 мая 2020

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

class ScreeningItem(models.Model):
    # other fields
    receivedItem = models.OneToOneField(ReceivedItem, null=True, on_delete=models.SET_NULL)

class ReceivedItem(models.Model):
    # other fields
    dossier = models.ForeignKey(Dossier, null=True, on_delete=models.SET_NULL)

class Dossier(models.Model):
    # other fields
    subjects = models.ManyToManyField('SubjectTypes', through='Subjects',
                                      through_fields=('dossier', 'subjectType'))

class Subject(models.Model):
    main = models.BooleanField(null=True)
    dossier = models.ForeignKey(Dossier, null=True, on_delete=models.SET_NULL)
    subjectType =  models.ForeignKey(SubjectType, null=True, on_delete=models.SET_NULL)

class SubjectType(models.Model):
    # other fields
    name = models.CharField(max_length=255, null=True, blank=True)
    parent = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)

Теперь проблема в том, что я должен найти в ScreeningItem элементах таблицы когда поле "далеко-далеко" SubjectType.name содержит определенные c слов. Нет, хуже. Как вы можете видеть ниже, в этой модели есть ссылка на себя «родитель-потомок», и я должен искать эти c слов в родственном SujectType, его родительском элементе и его прародителе, если они существуют.

Моя попытка:

exp = 'something'
queryset = ScreeningItem.objects.filter(
    Q(receivedItem__dossier__subjects__subjecttype__name__iregex=exp) |     
    Q(receivedItem__dossier__subjects__subjecttype__parent__name__iregex=exp) |     
    Q(receivedItem__dossier__subjects__subjecttype__parent__parent__name__iregex=exp))     

Однако, когда я получил количество записей намного меньше, чем я ожидал, я проверил базу данных и, к своему удивлению, обнаружил, что было много ScreeningItem, которые имели a ReceivedItem с Dossier, который был связан с SubjectTypes, в котором было слово, которое я искал.

К сожалению, мне не разрешено раскрывать содержание здесь. Итак, я написал процедуру тестирования ниже:

def test():
    exp = 'something'  # valid and equal both for Python and MySQL regular expression engines
    re_exp = re.compile(exp, re.IGNORECASE)
    queryset_1 = ScreeningItem.objects.filter(
        Q(receivedItem__dossier__subjects__subjecttype__name__iregex=exp) |     
        Q(receivedItem__dossier__subjects__subjecttype__parent__name__iregex=exp) |     
        Q(receivedItem__dossier__subjects__subjecttype__parent__parent__name__iregex=exp))     
    set_1 = set(queryset_1.values_list('id', flat=True))
    print(len(set_1))

    queryset_2 = GnomoItemTriagem.objects.filter(receivedItem__dossier__isnull=False)
    set_2a = set()
    set_2b = set()
    for item in queryset_2:
        subjects = item.receivedItem.dossier.subjects
        if subjects.filter(
                Q(name__iregex=exp) |
                Q(parent__name__iregex=exp) |
                Q(parent__parent__name__iregex=exp)).count() > 0:
            set_2a.add(item.id)

        for subject in subjects.all():
            if re_exp.findall(subject.name) or\
                (subject.parent and re_exp.findall(subject.parent.name)) or \
                    (subject.parent and subject.parent.parent and re_exp.findall(subject.parent.parent.name)):
                set_2b.add(item.id)

    print(len(set_2a))
    print(len(set_2b))

И тогда мои результаты были

1596
21223
21223

Итак, как должен быть написан мой 1-й запрос, чтобы также вернуть все необходимые 21223 элемента? Что я делаю не так?

1 Ответ

1 голос
/ 07 мая 2020

Поскольку subjects является полем «многие ко многим» по отношению к SubjectType, оно уже «приземляется» на эту модель. Причина, по которой вы можете запросить другой __subjecttype, заключается в том, что он обращается к ForeignKey из parent в «обратном» порядке.

Таким образом, ваш запрос должен выглядеть так:

queryset = ScreeningItem.objects.filter(
    Q(receivedItem__dossier<b>__subjects</b>__name__iregex=exp) |     
    Q(receivedItem__dossier<b>__subjects</b>__parent__name__iregex=exp) |     
    Q(receivedItem__dossier<b>__subjects</b>__parent__parent__name__iregex=exp)
)

Причина отсутствия ошибки в том, что ваше отношение parent не имеет related_name. Это означает, что по умолчанию related_name_query для вашего отношения parent - subjecttype. В результате вы делаете запрос, где вы ищете ScreeningItem с receivedItem с dossier с SubjectType a дочерним SubjectType, имеющим имя запроса, или родительский элемент этого ребенка и т. д. c. Таким образом, часть child идет не так, как надо.

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