我有一个基本的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属性都是相同的值。事实上,带注释的'count'等于用户生成的事件数量,无论类型如何。所以看起来我的具体是
authusage = baseusage.exclude(loggedevent__authevent__id__lt=1)
不会被子类排除。我试过id__lt = 1,id__isnull = True,等等。 HALP。
答案 0 :(得分:4)
Django模型继承的关键是要记住,使用非抽象基类所有实际上是基类的一个实例,可能碰巧有一些额外的数据绑在一边表。这意味着当您对基表执行搜索时,您将获得基类的实例,并且如果不对子类表执行重复的数据库查询以查看它们是否包含具有匹配键的记录,则无法确定它是哪个子类( “我有一个事件。它是否在AuthEvent中有记录?不,LDAP事件怎么样?......”)。除此之外,这意味着您无法在基类的普通查询中轻松过滤它们,而无需在每个子类表上进行连接。
您有两个选择:一个只是在子类上进行查询并计算结果(ldap_event_count = LDAPEvent.objects.filter(user=foo).count()
,...),这可能足以用于单个报告。我通常建议在基类中添加一个内容类型字段,这样你就可以有效地告诉实例哪个特定的子类而不必再进行另一个查询:
content_type = models.ForeignKey("contenttypes.ContentType")
这允许两个主要的改进:最常见的一个是你可以一般地处理许多事件而不必做类似于特定于子类的访问器(例如event.authevent
或event.ldapevent
)和处理DoesNotExist
。在这种情况下,它也会使重写查询变得微不足道,因为您可以像Event.objects.aggregate(Count("content_type"))
那样获取报告值,如果您的逻辑变得更复杂(“事件是Auth或LDAP和......”),这将变得特别方便。 “)。