对集合使用优化的 exists()
Using optimized exists() for a set
我想添加一个 属性 到我的 Person
class 那个 returns 一个布尔值是否这个人是否有某个 AntecedentTag
.
有关信息,我的简化模型如下:
class Person(models.Model):
antecedent_tags = models.ManyToManyField(AntecedentTag, verbose_name=u"Tags", through='AntecedentInfo')
class AntecedentInfo(models.Model):
antecedent_tag = models.ForeignKey(AntecedentTag)
person = models.ForeignKey(Person)
class AntecedentTag(models.Model):
name = models.CharField(max_length="64")
在我的个人 class 中,我正在考虑添加一个 @property
,如下所示。它可以工作,但是如果我必须在比方说 250 个人的列表中使用它,恐怕在性能方面这不会是最好的。
@property
def is_diabetic(self):
try:
if self.antecedentinfo_set.get(antecedent_tag__name="Diabetes"):
return True
except:
return False
问题:有什么方法可以在set
上使用优化的exists()
?
我尝试了以下但没有成功:
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes")
<AntecedentInfo: Diabetes: >
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes").exists()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'AntecedentInfo' object has no attribute 'exists'
我想到了count()
,但如果这个人没有得病,那是行不通的:
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes")
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "xx\lib\site-packages\django\db\models\manager.py", line 127, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "xx\lib\site-packages\django\db\models\query.py", line 334, in get
self.model._meta.object_name
DoesNotExist: AntecedentInfo matching query does not exist.
exists()
是查询集的方法,而不是模型实例的方法。所以你需要调用 filter()
而不是 get()
.
为什么要使用antecedentinfo_set
中间关系?使用 M2M 字段:
@property
def is_diabetic(self):
return self.antecedent_tags.filter(name="Diabetes").exists()
In my Person class, I was thinking of adding a @property
like the
below. It works, but if I have to use this on a list of let's say 250
individual persons, I'm afraid performance wise this is not going to
be the best.
我可以想到两种方法,可以让您免于为一组 250 人数 250 次。
1。使用 .extra
我假设您还没有任何自定义 Managers/QuerySets,为了代码的最大 DRY-ness,最好将下一行代码放入自定义 Manager/QuerySet (将它写到视图中仍然可以,但是如果你想在其他地方重用这个功能,你将不得不重复它。)。
因此我们将创建一个自定义查询集 class。如果您不熟悉该主题,可以在 Django Managers
阅读更多内容
class PersonQuerySet(models.QuerySet):
#this adds a new field "has_"+disease
#for every object in our queryset
def annotate_disease_existence(self, disease):
has_disease = 'has_'+disease
return self.extra(select={
has_disease: "SELECT count(*) \
FROM someapp_antecedentinfo \
LEFT JOIN someapp_antecedenttag \
ON someapp_antecedenttag.id = someapp_antecedentinfo.antecedent_tag_id
WHERE someapp_antecedentinfo.person_id = someapp_person.id \
AND someapp_antecedenttag.name = '%s" % disease
})
#now lets use it
class Person(models.Model):
#...
objects = PersonQuerySet.as_manager()
如何使用?
我们编写的这个自定义方法使我们能够针对每个 Person
QuerySet 调用它。所以我们可以这样做:
def my_special_view(request):
my250persons = Person.objects.annotate_disease_existence("Diabetes")[:250]
for person in my250persons:
print "{} has Diabetes? {}".format(
person,
'Yes' if person.has_diabetes else 'No'
)
2。使用 .prefetch_related
我希望您已经看到我们在第一个示例中所做的事情 - 我们使用 subquery
在 Person
中添加了一些额外的信息,如果他有病的话。但是当我们真正想要使用整个 Disease (在我们的例子中是 Tag 或 Info) 对象时,问题就来了。然后我们可以将 prefetch_related
与自定义 Prefetch
对象一起使用。
class PersonQuerySet(models.QuerySet):
def prefetch_disease(self, disease):
disease_set = disease.lower() + '_set'
return self.prefetch_related(Prefetch(
#depending on our needs
'antecedent_tags', # OR 'antecedentinfo_set'
queryset = AntecedentTag.objects.filter(name=disease),
#OR queryset = AntecedentInfo.objects.filter(antecedent_tag__name=disease)
to_attr = disease_set
))
如何使用?
使用这种方法时,我们会向 queryset
的每个 Person
添加一个新的对象列表。所以我们可以像这样访问它们:
def my_very_special_view(request):
my250persons = Person.objects.prefetch_disease("Diabetes")[:250]
for person in my250persons:
print "{} has Diabetes? {}! More Info: {}".format(
person,
'Yes' if person.diabetes_set else 'No',
person.diabetes_set[0]
)
好的,但它如何与单个对象一起使用?
def my_single_person_view(request, id):
person = get_object_or_404(Person.objects.prefetch_disease("Diabetes"),pk=id)
#or
person = get_object_or_404(Person.objects.annotate_disease_existence("Diabetes"),pk=id)
我想添加一个 属性 到我的 Person
class 那个 returns 一个布尔值是否这个人是否有某个 AntecedentTag
.
有关信息,我的简化模型如下:
class Person(models.Model):
antecedent_tags = models.ManyToManyField(AntecedentTag, verbose_name=u"Tags", through='AntecedentInfo')
class AntecedentInfo(models.Model):
antecedent_tag = models.ForeignKey(AntecedentTag)
person = models.ForeignKey(Person)
class AntecedentTag(models.Model):
name = models.CharField(max_length="64")
在我的个人 class 中,我正在考虑添加一个 @property
,如下所示。它可以工作,但是如果我必须在比方说 250 个人的列表中使用它,恐怕在性能方面这不会是最好的。
@property
def is_diabetic(self):
try:
if self.antecedentinfo_set.get(antecedent_tag__name="Diabetes"):
return True
except:
return False
问题:有什么方法可以在set
上使用优化的exists()
?
我尝试了以下但没有成功:
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes")
<AntecedentInfo: Diabetes: >
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes").exists()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'AntecedentInfo' object has no attribute 'exists'
我想到了count()
,但如果这个人没有得病,那是行不通的:
>>> p.antecedentinfo_set.get(antecedent_tag__name="Diabetes")
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "xx\lib\site-packages\django\db\models\manager.py", line 127, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "xx\lib\site-packages\django\db\models\query.py", line 334, in get
self.model._meta.object_name
DoesNotExist: AntecedentInfo matching query does not exist.
exists()
是查询集的方法,而不是模型实例的方法。所以你需要调用 filter()
而不是 get()
.
为什么要使用antecedentinfo_set
中间关系?使用 M2M 字段:
@property
def is_diabetic(self):
return self.antecedent_tags.filter(name="Diabetes").exists()
In my Person class, I was thinking of adding a
@property
like the below. It works, but if I have to use this on a list of let's say 250 individual persons, I'm afraid performance wise this is not going to be the best.
我可以想到两种方法,可以让您免于为一组 250 人数 250 次。
1。使用 .extra
我假设您还没有任何自定义 Managers/QuerySets,为了代码的最大 DRY-ness,最好将下一行代码放入自定义 Manager/QuerySet (将它写到视图中仍然可以,但是如果你想在其他地方重用这个功能,你将不得不重复它。)。
因此我们将创建一个自定义查询集 class。如果您不熟悉该主题,可以在 Django Managers
阅读更多内容class PersonQuerySet(models.QuerySet):
#this adds a new field "has_"+disease
#for every object in our queryset
def annotate_disease_existence(self, disease):
has_disease = 'has_'+disease
return self.extra(select={
has_disease: "SELECT count(*) \
FROM someapp_antecedentinfo \
LEFT JOIN someapp_antecedenttag \
ON someapp_antecedenttag.id = someapp_antecedentinfo.antecedent_tag_id
WHERE someapp_antecedentinfo.person_id = someapp_person.id \
AND someapp_antecedenttag.name = '%s" % disease
})
#now lets use it
class Person(models.Model):
#...
objects = PersonQuerySet.as_manager()
如何使用?
我们编写的这个自定义方法使我们能够针对每个 Person
QuerySet 调用它。所以我们可以这样做:
def my_special_view(request):
my250persons = Person.objects.annotate_disease_existence("Diabetes")[:250]
for person in my250persons:
print "{} has Diabetes? {}".format(
person,
'Yes' if person.has_diabetes else 'No'
)
2。使用 .prefetch_related
我希望您已经看到我们在第一个示例中所做的事情 - 我们使用 subquery
在 Person
中添加了一些额外的信息,如果他有病的话。但是当我们真正想要使用整个 Disease (在我们的例子中是 Tag 或 Info) 对象时,问题就来了。然后我们可以将 prefetch_related
与自定义 Prefetch
对象一起使用。
class PersonQuerySet(models.QuerySet):
def prefetch_disease(self, disease):
disease_set = disease.lower() + '_set'
return self.prefetch_related(Prefetch(
#depending on our needs
'antecedent_tags', # OR 'antecedentinfo_set'
queryset = AntecedentTag.objects.filter(name=disease),
#OR queryset = AntecedentInfo.objects.filter(antecedent_tag__name=disease)
to_attr = disease_set
))
如何使用?
使用这种方法时,我们会向 queryset
的每个 Person
添加一个新的对象列表。所以我们可以像这样访问它们:
def my_very_special_view(request):
my250persons = Person.objects.prefetch_disease("Diabetes")[:250]
for person in my250persons:
print "{} has Diabetes? {}! More Info: {}".format(
person,
'Yes' if person.diabetes_set else 'No',
person.diabetes_set[0]
)
好的,但它如何与单个对象一起使用?
def my_single_person_view(request, id):
person = get_object_or_404(Person.objects.prefetch_disease("Diabetes"),pk=id)
#or
person = get_object_or_404(Person.objects.annotate_disease_existence("Diabetes"),pk=id)