在Django中注释多表继承

ldl*_*ork 5 django django-models django-orm

我有一个基本的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)
Run Code Online (Sandbox Code Playgroud)

用户在执行相关操作时会生成这些事件.我正在尝试生成一个使用情况报告,显示每个用户在上个月造成的每种事件类型的数量.我正在与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))
Run Code Online (Sandbox Code Playgroud)

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

authusage = baseusage.exclude(loggedevent__authevent__id__lt=1)
Run Code Online (Sandbox Code Playgroud)

不被子类排除.我试过id__lt = 1,id__isnull = True,等等.HALP.

Chr*_*ams 4

Django 模型继承的关键是记住,对于非抽象基类,所有内容实际上都是基类的实例,可能碰巧有一些来自单独表的额外数据。这意味着,当您在基表上进行搜索时,您会返回基类的实例,并且如果不对子类表进行重复的数据库查询以查看它们是否包含带有匹配键(“我有一个事件。它在 AuthEvent 中有记录吗?没有。LDAP 事件怎么样?\xe2\x80\xa6”)。除此之外,这意味着如果不对每个子类表进行联接,则无法在基类的正常查询中轻松过滤它们。

\n\n

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

\n\n

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

\n\n

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

\n