我想在我的Person
类中添加一个属性,该属性返回一个布尔值,无论该人是否具有某个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")
在我的Person课程中,我正在考虑添加@property
,如下所示。它有效,但如果我必须在250个人的名单上使用这个,我害怕表现明智,这不会是最好的。
@property
def is_diabetic(self):
try:
if self.antecedentinfo_set.get(antecedent_tag__name="Diabetes"):
return True
except:
return False
问题:有没有办法在exists()
上使用优化的set
?
我尝试了以下但没有成功:
>>> 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.
答案 0 :(得分:1)
exists()
是查询集的方法,而不是模型实例的方法。因此,您需要拨打filter()
而不是get()
。
为什么使用antecedentinfo_set
中间关系?使用M2M字段:
@property
def is_diabetic(self):
return self.antecedent_tags.filter(name="Diabetes").exists()
答案 1 :(得分:0)
在我的Person课程中,我考虑添加
@property
之类的 下面。它有效,但如果我必须在250的列表中使用它 个人,我害怕表现明智,这是不会的 是最好的。
我可以想到两种方法可以帮助你节省250次250人的计数。
.extra
我认为您还没有任何自定义管理器/查询集,为了最大程度地减少代码干扰,最好将下一行代码放入自定义Manager / QuerySet (它将其写入View仍然可以,但是如果你想在其他地方重复使用这个功能,你将不得不重复它。)。
因此,我们将创建一个自定义QuerySet类。如果您不熟悉该主题,可以在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'
)
.prefetch_related
我希望你已经看到我们在第一个例子中做了什么 - 我们使用了subquery
如果他有某种疾病就向Person
添加额外的信息。但是当我们真正想要使用整个疾病(在我们的案例中为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
))
如何使用?
使用此方法时,我们会向Person
的每个queryset
添加一个新的对象列表。所以我们可以这样访问它们:
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)