对集合使用优化的exists()

时间:2015-04-19 08:49:42

标签: django django-models

我想在我的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.

2 个答案:

答案 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人的计数。

1。使用.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'
        )

2。使用.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)