5

我有一个基本的 LoggedEvent 模型和许多子类模型,如下所示:

class LoggedEvent(models.Model):
    user = models.ForeignKey(User, blank=True, null=True)
    timestamp = models.DateTimeField(auto_now_add=True)

class AuthEvent(LoggedEvent):
    good = models.BooleanField()
    username = models.CharField(max_length=12)

class LDAPSearchEvent(LoggedEvent):
    type = models.CharField(max_length=12)
    query = models.CharField(max_length=24)

class PRISearchEvent(LoggedEvent):
    type = models.CharField(max_length=12)
    query = models.CharField(max_length=24)

用户在执行相关操作时会生成这些事件。我正在尝试生成每个用户在上个月引起的每种事件类型的使用情况报告。我在 Django 的 ORM 上苦苦挣扎,当我接近时,我遇到了一个问题。下面是查询代码:

def usage(request):
    # Calculate date range
    today = datetime.date.today()
    month_start = datetime.date(year=today.year, month=today.month - 1, day=1)
    month_end = datetime.date(year=today.year, month=today.month, day=1) - datetime.timedelta(days=1)

    # Search for how many LDAP events were generated per user, last month
    baseusage = User.objects.filter(loggedevent__timestamp__gte=month_start, loggedevent__timestamp__lte=month_end)
    ldapusage = baseusage.exclude(loggedevent__ldapsearchevent__id__lt=1).annotate(count=Count('loggedevent__pk'))
    authusage = baseusage.exclude(loggedevent__authevent__id__lt=1).annotate(count=Count('loggedevent__pk'))

    return render_to_response('usage.html', {
        'ldapusage' : ldapusage,
        'authusage' : authusage,
    }, context_instance=RequestContext(request))

ldapusage 和 authusage 都是用户列表,每个用户都使用 .count 属性进行注释,该属性应该表示用户生成了多少特定事件。但是,在这两个列表中, .count 属性是相同的值。事实上,带注释的“计数”等于用户生成的事件数,无论类型如何。所以看起来我的具体

authusage = baseusage.exclude(loggedevent__authevent__id__lt=1)

不按子类排除。我尝试过 id__lt=1、id__isnull=True 等。哈尔普。

4

1 回答 1

4

Django 模型继承的关键是记住,对于非抽象基类,所有东西实际上都是基类的一个实例,它可能碰巧有一些额外的数据从单独的表中捆绑在一边。这意味着当您在基表上进行搜索时,您将返回基类的实例,并且如果不对子类表进行重复的数据库查询以查看它们是否包含具有匹配键的记录,就无法判断它是哪个子类( “我有一个事件。它在 AuthEvent 中有记录吗?没有。LDAP 事件呢?...”)。除其他外,这意味着如果不对每个子类表进行连接,就无法在基类的普通查询中轻松过滤它们。

您有两种选择:一种是简单地对子类进行查询并计算结果 ( ldap_event_count = LDAPEvent.objects.filter(user=foo).count(), ...),这对于单个报告可能就足够了。我通常建议在基类中添加一个内容类型字段,这样您就可以有效地判断实例是哪个特定子类,而无需执行其他查询:

content_type = models.ForeignKey("contenttypes.ContentType")

这允许两个主要改进:最常见的一个是您可以通用地处理许多事件,而不必执行诸如命中子类特定访问器(例如event.autheventor event.ldapevent)和处理之类的事情DoesNotExist。在这种情况下,重写查询也变得很简单,因为您可以执行诸如Event.objects.aggregate(Count("content_type"))获取报告值之类的操作,如果您的逻辑变得更加复杂(“事件是 Auth 或 LDAP 和……”),这将变得特别方便。

于 2010-08-24T11:44:57.050 回答