【发布时间】: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__作为“前缀”。现在看起来您正在搜索名为exp的subjects。 (也许模型的某些部分您没有分享)? -
@WillemVanOnsem
subjects是Dossier模型中的字段,通过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