Аннотировать в 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')
                )

Это должно вернуть словарь счетчиков против идентификатора типа контента, довольно близкий к тому, что вы хотите.

Другие вопросы по тегам