我必须对一个大型数据库进行查询,其建模有些复杂,下面我将尝试对其进行删减。
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
包含具体的词语。不,更糟糕。如下图所示,在该模型中,有一个父子自述,我必须在相关的 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
那么,我的第1个查询应该怎么写,才能同时返回所有需要的21223项?我到底做错了什么?
自 subjects
是一个多对多的领域,以 SubjectType
,它已经 "落地 "在该型号。的原因是,你可以查询另一个 __subjecttype
是因为它在访问 ForeignKey
的 parent
在 "反向"。
因此,你的查询应该是这样的。
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
. 所以,这意味着,默认的 related_name_query
为您 parent
关系是 subjecttype
. 结果你做了一个查询,你在哪里寻找 ScreeningItem
附带 receivedItem
附带 dossier
以 SubjectType
a 孩子 SubjectType
的名称,或其父名为 该 儿等。的 孩子 部分是因此出了问题。