【问题标题】:Django query omitting expected resultsDjango查询省略了预期结果
【发布时间】:2020-08-23 07:11:55
【问题描述】:

我必须在一个模型复杂的大型数据库上执行查询,我将在下面尝试对其进行删节:

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 表项中找到 far, far away 相关字段 SubjectType.name 包含特定单词。不,更糟。正如您在下面看到的那样,该模型中有一个父子自引用,我必须在相关的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 有一个 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

那么我的第一个查询应该如何编写才能返回所有需要的 21223 项?我做错了什么?

【问题讨论】:

  • 您需要提供样本、经过编辑的数据,并向我们展示您获得的结果和您期望的结果。
  • 第二个不应该是 `Q(subjecttype__name__iregex=exp)` 所以在过滤中使用subjecttype__ 作为“前缀”。现在看起来您正在搜索名为expsubjects。 (也许模型的某些部分您没有分享)?
  • @WillemVanOnsem subjectsDossier 模型中的字段,通过Subject 模型将“多对多”与SubjectTypemodel 相关联,该模型是链接表。
  • @VBobCat: 啊,但是我猜应该是receivedItem__dossier__subjects__name__iregex=...
  • @VBobCat:因为您的模型名称是subjecttype。由于您没有为parent 定义related_name,因此父级“反向”中关系名称的默认名称为__subjecttype。所以你在这里基本上查询了一个 ScreeningItem 和一个 receivedItem 和一个 dossier 和一个 SubjectType 一个 child 主题类型,它有一个名称的查询,或者 that 孩子等

标签: python mysql django django-orm


【解决方案1】:

由于subjectsSubjectType 的多对多字段,它已经“登陆”该模型。之所以可以查询另一个__subjecttype,是因为它正在“反向”访问parentForeignKey

因此,您的查询应如下所示:

queryset = ScreeningItem.objects.filter(
    Q(receivedItem__dossier__subjects__name__iregex=exp) |     
    Q(receivedItem__dossier__subjects__parent__name__iregex=exp) |     
    Q(receivedItem__dossier__subjects__parent__parent__name__iregex=exp)
)

它没有出错的原因是因为您的parent 关系没有related_name。这意味着您的parent 关系的默认related_name_querysubjecttype。结果,您进行查询,在其中查找 ScreeningItemreceivedItemdossierSubjectType 一个 child SubjectType 具有查询名称,或那个子的父级,等等。child部分就是它出错的地方。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-07
    • 2021-07-20
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    相关资源
    最近更新 更多