【问题标题】:Django queryset subset with custom manager?带有自定义管理器的 Django 查询集子集?
【发布时间】:2016-07-28 16:40:31
【问题描述】:

我有类似的模型:

class BaseInfo(models.Model):
  is_active = models.BooleanField(default=True)
  # other fields..
  class Meta:
     abstract = True

class Customer(BaseInfo):
   name = models.CharField(max_length=50)
   ## other fields..

在模板中我想显示这个模型的表格,但我想突出显示不活动的表格。所以我在模板中有这个片段:

{% for c in customers %}
    <tr {%if not c.is_active %}class="not-active" {%endif%}>
      <td>..</td>
    </tr>
{% endfor %}

现在,我想在此旁边显示活跃的 NUMBER 个。 我可以这样做:

all = Customer.objects.filter(name="foo")
excludeInactive = all.filter(is_active=False)

然后在上下文中传递两者。 但我更喜欢模板中这样的东西:

{{customers.exclude_deleted.count}}

或者也许: {{customers.exclude_deleted|length}}?

我有更多模型继承了这个抽象类。所以我认为基类的经理可以工作吗?我只是不明白如何写一个..
另外,性能怎么样?如果我对.filter() 进行两次调用,会导致两个数据库查询,即使第二个查询是已评估查询集的子集?

【问题讨论】:

  • 看看自定义模板标签
  • 您可能需要为此目的查看自定义管理器。

标签: django django-models filter subset django-queryset


【解决方案1】:

您可以通过多种方式做到这一点。其中一些:

  • 在视图中进行所有计算并将准备好的数据传递给上下文:

    customers = Customer.objects.filter(name="foo")
    active_customers_count = customers.filter(is_active=True)
    
  • 自定义模板标签(docs):

    标签

    from django import template
    
    register = template.Library()
    
    
    @register.assignment_tag
    def filter_qs(qs, **kwargs):
        return qs.filter(**kwargs)
    

    模板

    {% filter_qs customers is_active=True as active_customers %}
    {{ active_customers|length }}            
    
  • 带有自定义查询集的自定义管理器 (docs):

    型号

    class IsActiveQuerySet(models.QuerySet):
        def active(self):
            return self.filter(is_active=True)
    
    
    class IsActiveManager(models.Manager):
        def get_queryset(self):
            return IsActiveQuerySet(self.model, using=self._db)
    
    
    class Customer(BaseInfo):
       objects = IsActiveManager()
       name = models.CharField(max_length=50)
    

    模板

    {{ customers.active|length }}
    

我强烈建议您使用第一个选项,这将是最“Pythonic”的方式。 无论如何,django 将为上面列出的任何解决方案进行 2 个单独的 sql 查询。

【讨论】:

  • 在经理解决方案中,我可能会建议将active改为inactive
  • 谢谢@JaredGoguen,这只是一个错字。
  • 提醒一下,我确实希望同一个表中的非活动和活动的..所以如果你覆盖对象属性,我将无法访问非活动的?
  • 通过调用Customer.objects.all(),您将获得所有(活动、非活动)对象。但是使用Customer.objects.all().filter(foo=bar).active(),您将获得活动对象。基本上我们只是应用了额外的过滤器,但将其封装在 queryset 方法中。
  • 在性能方面,我可以避免第二次查询吗?我的意思是我可以在 python 中过滤活动的那些。因为它们是原始查询的子集。虽然我只需要计算活动的,所以调用 .count 而不是 |length 会更好吗?
猜你喜欢
  • 2018-03-08
  • 2016-11-24
  • 2022-10-19
  • 1970-01-01
  • 2019-05-26
  • 1970-01-01
  • 2010-10-14
  • 2016-09-11
  • 1970-01-01
相关资源
最近更新 更多