带有计数注释的 django admin list_display
django admin list_display with count annotation
我想在 Django Admin 中将 "annotation count" 字段添加到 list_display 中。
models.py
class Log(models.Model):
...
ip_address = models.GenericIPAddressField(
verbose_name=_('IP address'),
db_index=True,
)
...
admin.py
class LogAdmin(admin.ModelAdmin):
list_display = (..., 'ip_address', 'ip_address_count', ...)
def ip_address_count(self, instance):
return models.Log.objects \
.filter(ip_address=instance.ip_address) \
.count()
ip_address_count.short_description = _('IP Address Count')
它适用于 "LOTS OF" SQL 查询。
我想像这样改进我的 admin.py
:
class Log(models.Model):
...
def get_queryset(self, request):
qs = super(LogAdmin, self).get_queryset(request)
qs = qs.values('ip_address') \
.annotate(ip_address_count=Count('ip_address'))
return qs
def ip_address_count(self, instance):
return instance.ip_address_count
但是,我遇到了错误信息:
'dict' object has no attribute '_meta'
看来找到错误原因了:
但是,它并没有帮助我解决问题。
感谢您的回答。
编辑:如果我不附加 .values('ip_address)
.
,它会出错
它没有group by ip_address
。它 group by
所有字段 group by field1, field2, ip_address, ...
因此,它导致所有情况都是“1”。
那是因为当您将 values
应用于 queryset
时,它 returns 是一个字典列表,而管理员期望的是 objects
的查询集。
我以前遇到过这种情况,所以我做了这样的事情:
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(request, extra_context)
try:
# this is the final queryset to be rendered on the page after pagination.
_cl = response.context_data['cl']
qs = _cl.result_list._clone()
# since in my case mysql 5.5 does'nt support subquery with LIMIT
# fetch all the ips on that page
ips = list(set([obj.ip_address for obj in qs]))
result_qs = models.Log.objects.values('ip_address') \
.filter(ip_address__in=ips) \
.annotate(ip_address_count=Count('ip_address'))
result = {_r['ip_address']: _r['ip_address_count'] for _r in result_qs}
setattr(self, '_ip_addr_count', result)
except:
pass
return response
def ip_address_count(self, instance):
# fetch the count from the dict we set above
return self._ip_addr_count.get(instance.ip_address)
因此你看到我所做的是,从最终查询集中获取 ips,然后使用它来查询计数。通过这样做,您将每页只查询一次数据库。
希望大家根据自己的需要得到基础idea.Please适合的
以下是您可以在本地执行的操作。 Django 知道如何处理这个(请参阅有关 admin and list_display 的文档)。
class LogAdmin(admin.ModelAdmin):
list_display = (..., "field1", "field2"... "quantity_of_things",)
def get_queryset(self, request):
return (super(LogAdmin, self).get_queryset(request).annotate(quantity_of_things=Count("xxxxxx")))
def quantity_of_things(self, obj):
return obj.quantity_of_things
quantity_of_things.short_description = "Number of xxxxxx"
你可以用你需要的任何东西来注释查询集(例如 Count
)。如您所见,您只需使用相同的名称 3 次(在我的示例中为 quantity_of_things
)
- 在list_display(Django 理解这是一个可调用的),
- 在注释中,
- 在要调用的函数中
quantity_of_things.short_description
用于列标题
我想在 Django Admin 中将 "annotation count" 字段添加到 list_display 中。
models.py
class Log(models.Model):
...
ip_address = models.GenericIPAddressField(
verbose_name=_('IP address'),
db_index=True,
)
...
admin.py
class LogAdmin(admin.ModelAdmin):
list_display = (..., 'ip_address', 'ip_address_count', ...)
def ip_address_count(self, instance):
return models.Log.objects \
.filter(ip_address=instance.ip_address) \
.count()
ip_address_count.short_description = _('IP Address Count')
它适用于 "LOTS OF" SQL 查询。
我想像这样改进我的 admin.py
:
class Log(models.Model):
...
def get_queryset(self, request):
qs = super(LogAdmin, self).get_queryset(request)
qs = qs.values('ip_address') \
.annotate(ip_address_count=Count('ip_address'))
return qs
def ip_address_count(self, instance):
return instance.ip_address_count
但是,我遇到了错误信息:
'dict' object has no attribute '_meta'
看来找到错误原因了:
但是,它并没有帮助我解决问题。
感谢您的回答。
编辑:如果我不附加 .values('ip_address)
.
它没有group by ip_address
。它 group by
所有字段 group by field1, field2, ip_address, ...
因此,它导致所有情况都是“1”。
那是因为当您将 values
应用于 queryset
时,它 returns 是一个字典列表,而管理员期望的是 objects
的查询集。
我以前遇到过这种情况,所以我做了这样的事情:
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(request, extra_context)
try:
# this is the final queryset to be rendered on the page after pagination.
_cl = response.context_data['cl']
qs = _cl.result_list._clone()
# since in my case mysql 5.5 does'nt support subquery with LIMIT
# fetch all the ips on that page
ips = list(set([obj.ip_address for obj in qs]))
result_qs = models.Log.objects.values('ip_address') \
.filter(ip_address__in=ips) \
.annotate(ip_address_count=Count('ip_address'))
result = {_r['ip_address']: _r['ip_address_count'] for _r in result_qs}
setattr(self, '_ip_addr_count', result)
except:
pass
return response
def ip_address_count(self, instance):
# fetch the count from the dict we set above
return self._ip_addr_count.get(instance.ip_address)
因此你看到我所做的是,从最终查询集中获取 ips,然后使用它来查询计数。通过这样做,您将每页只查询一次数据库。
希望大家根据自己的需要得到基础idea.Please适合的
以下是您可以在本地执行的操作。 Django 知道如何处理这个(请参阅有关 admin and list_display 的文档)。
class LogAdmin(admin.ModelAdmin):
list_display = (..., "field1", "field2"... "quantity_of_things",)
def get_queryset(self, request):
return (super(LogAdmin, self).get_queryset(request).annotate(quantity_of_things=Count("xxxxxx")))
def quantity_of_things(self, obj):
return obj.quantity_of_things
quantity_of_things.short_description = "Number of xxxxxx"
你可以用你需要的任何东西来注释查询集(例如 Count
)。如您所见,您只需使用相同的名称 3 次(在我的示例中为 quantity_of_things
)
- 在list_display(Django 理解这是一个可调用的),
- 在注释中,
- 在要调用的函数中
quantity_of_things.short_description
用于列标题