Аннотировать в Django с родовыми отношениями - PullRequest
1 голос
/ 19 ноября 2010

Я использую 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 независимо от диапазона времени.

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 Ответ

1 голос
/ 19 ноября 2010

Возможно, для вашей цели будет более эффективным добавить общее отношение к Hit модели:

class Hit(models.Model):
    ...
    object_id = models.PositiveIntegerField()
    content_type = models.ForeignKey(ContentType)
    content_object = generic.GenericForeignKey('content_type', 'object_id')

, а затем выполнить запрос count () для попадания напрямую:

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')
                )

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...