Аннотировать в Django с родовыми отношениями
Я использую django-hitcount, чтобы подсчитать попадания в мои объекты базы данных. Я хочу посчитать число попаданий по объекту, чтобы определить, какой объект имеет наибольшее количество попаданий в заданном диапазоне времени. Приложение имеет две модели, представляющие интерес здесь:
class Hit(models.Model):
created = models.DateTimeField(editable=False)
ip = models.CharField(max_length=40, editable=False)
session = models.CharField(max_length=40, editable=False)
user_agent = models.CharField(max_length=255, editable=False)
user = models.ForeignKey(User,null=True, editable=False)
hitcount = models.ForeignKey(HitCount, editable=False)
class HitCount(models.Model):
hits = models.PositiveIntegerField(default=0)
modified = models.DateTimeField(default=datetime.datetime.utcnow)
content_type = models.ForeignKey(ContentType,
verbose_name="content cype",
related_name="content_type_set_for_%(class)s",)
object_pk = models.TextField('object ID')
content_object = generic.GenericForeignKey('content_type', 'object_pk')
"Hit" регистрирует каждое попадание с отметкой времени, в то время как HitCount хранит общее количество попаданий. Чтобы получить попадания по объекту и за определенный промежуток времени, мне нужно сделать следующее:
Фильтр Число попаданий по дате создания Количество запросов в каждом объекте content_object (в пределах временного диапазона, указанного выше), рассчитанное выше, возвращаемое content_object и count
Это может быть очень дорого, поэтому я планировал выполнять калькуляцию / кэширование один раз в день.
В качестве первого шага я хотел посчитать количество обращений на каждый объект content_object независимо от временного диапазона.
limited_hc = Hit.objects.all().values('hitcount__content_object').annotate(count = Count('hitcount__object_pk'))
Я сразу же столкнулся с проблемой:
Невозможно разрешить ключевое слово hitcount__content_object в поле. Варианты выбора: созданный, hitcount, id, ip, сессия, пользователь, user_agent
После некоторого поиска я обнаружил, что аннотации и родовые отношения не очень хорошо работают вместе. Если я использую object_pk вместо content_object, он работает нормально, но тогда у меня нет имени объекта.
Итак, мой вопрос: что является альтернативой для достижения того же результата? Как можно группировать по объектам, но также сохранять имя?
У меня есть модель (content_type) и идентификатор (object_pk), поэтому я всегда могу вытащить их по отдельности, но это выглядит не элегантно.,,
1 ответ
Возможно, для вашей цели будет более эффективно добавить общее отношение к Hit
модель:
class Hit(models.Model):
...
object_id = models.PositiveIntegerField()
content_type = models.ForeignKey(ContentType)
content_object = generic.GenericForeignKey('content_type', 'object_id')
и затем запустите запрос count() для Hit напрямую:
t = ContentType.objects.get_for_model(the_object_being_hit)
id = the_object_being_hit.id
count = Hit.objects.filter(
created__range=(from_timestamp, to_timestamp),
content_type = t,
object_id = id
).count()
Вы можете использовать систему миграции Django South для изменения модели количества обращений. Вы также можете попытаться создать подкласс Hit после мартышки-патча его мета-класса или просто определить свои собственные модели, которые лучше соответствуют вашим потребностям.
edit Если вы хотите посчитать попадания для целого класса объектов или нескольких классов, то вы можете иметь:
count = Hit.objects.filter(
created__range = myrange,
content_type__in = set_of_types
).count()
Где set_of_types
может быть либо список, построенный с get_for_model
вызовы или набор запросов, полученных путем прямой фильтрации ContentType
Таблица.
Приятной частью метода count() является то, что он делает подсчет в базе данных, что намного быстрее.
Чтобы получить разбивку по типу контента, попробуйте это:
counts = Hit.objects.filter(
created__range = myrange
).values(
'content_type'
).annotate(
Count('content_type')
)
Это должно вернуть словарь счетчиков против идентификатора типа контента, довольно близкий к тому, что вы хотите.