如何将键值对添加到Django queryset

时间:2019-07-11 10:49:25

标签: python django

我想在模板的for循环中显示每个帐户的统计信息。在字典或查询集列表中。

我有3种模型:(1)AdvertisingAccount,(2)Campaign,UserProfile。 我需要使用外键对属于某些AdvertisingAccounts的Campaigns的点击和视图求和。 (AdvertisingAccount也属于UserProfile)

我正在尝试检索,计算新的键值并将其添加到queryset的字典,但是似乎我无法在dict方法中做到这一点……

views.py

def ad_accounts(request):

    user = request.user
    accounts = user.userprofile.advertising_accounts.all()

    accounts = accounts.values()

    for d in accounts:
        d.update(('clicks', d.campaigns.all().aggregate(Sum('clicks')) )  for k,v in d.items())
        d.update(('views', d.campaigns.all().aggregate(Sum('views')) )  for k,v in d.items())

models.py


class AdvertisingAccount(models.Model):
    balance = models.DecimalField(max_digits=12, decimal_places=2, default=0, blank=True, null=True)
    foreign_system_name = models.CharField(max_length=30, blank=True, default=None)
    foreign_system_id = models.IntegerField(default=None, blank=True, null=True)
    def __str__(self):
        return self.foreign_system_name + '--' + str(self.foreign_system_id)


class Campaign(models.Model):
    userprofile = models.ForeignKey('users.UserProfile', on_delete=models.PROTECT, related_name='campaigns')
    trade = models.ForeignKey('payments.Trade', on_delete=models.PROTECT, related_name='campaigns')
    ad_account = models.ForeignKey('AdvertisingAccount', on_delete=models.PROTECT, related_name='campaigns')
    views = models.IntegerField(default=0, blank=True, null=True)
    clicks = models.IntegerField(default=0, blank=True, null=True)
    ad_spent = models.DecimalField(max_digits=12, decimal_places=2, default=0, blank=True, null=True)

    def __str__(self):
        return str(self.userprofile) + '--' + str(self.ad_account.foreign_system_name) + '--' + str(self.trade.ad_budget)

模板

{% for i in accounts %}
  {{i.clicks}}
  {{i.views}}
{% endfor %}

我收到错误消息:'dict' object has no attribute 'campaigns'

由于我无法从模板检索过滤的数据,因此我试图构建自定义查询集或字典列表。整个方法正确吗?如何解决我的问题?

1 个答案:

答案 0 :(得分:5)

请手动进行注释,您可以简单地使用Django的.annotate(..) method [Django-doc]。您甚至在这里使用了生成器,它会导致针对单个Account模型的查询时间很多

from django.db.models import Sum

accounts = user.userprofile.advertising_accounts.annotate(
    clicks=Sum('campaigns__clicks'),
    views=Sum('campaigns__views')
)

以上内容将生成QuerySetAdvertisingAccount,并具有额外的属性.clicks.views。实际上,该错误完全是因为您最初没有使用AdvertisingAccount,而是使用字典(这些字典源自.values(),因此您失去了关系的上下文)。

注释将在单个查询中运行,Django将在其中进行如下查询:

SELECT advertising_acount.*,
       SUM(campaign.clicks) AS clicks,
       SUM(campaign.views) AS views
FROM advertising_acount
LEFT OUTER JOIN campaign ON campaign.ad_account_id = advertising_acount.id
GROUP BY advertising_acount.id

这比汇总 per AdvertisingAccount更为有效,因为那样的话,您将在这里使用 n 2n + 1 次。 > AdvertisingAcount的数量。

因此,通常在字典上使用模型实例通常总是更好:模型类允许您定义方法,属性,添加关系等。它还保存对象的类型(例如{ 1}}),而具有相同键的两个字典本身并不源自相同的模型。字典只是将字段名称映射到相应数据库值的简单结构。因此,您“失去了”很多上下文。