Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

models.py 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from datetime import timedelta
  4. from django.db import models
  5. from django.conf import settings
  6. from django.db.models import F
  7. from django.utils import timezone
  8. from django.dispatch import receiver
  9. from django.utils.encoding import python_2_unicode_compatible
  10. from django.contrib.contenttypes.fields import GenericForeignKey
  11. from django.contrib.contenttypes.models import ContentType
  12. from django.utils.translation import ugettext_lazy as _
  13. AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
  14. from .managers import HitCountManager, HitManager
  15. from .signals import delete_hit_count
  16. @receiver(delete_hit_count)
  17. def delete_hit_count_handler(sender, instance, save_hitcount=False, **kwargs):
  18. """
  19. Custom callback for the Hit.delete() method.
  20. Hit.delete(): removes the hit from the associated HitCount object.
  21. Hit.delete(save_hitcount=True): preserves the hit for the associated
  22. HitCount object.
  23. """
  24. if not save_hitcount:
  25. instance.hitcount.decrease()
  26. @python_2_unicode_compatible
  27. class HitCount(models.Model):
  28. """
  29. Model that stores the hit totals for any content object.
  30. """
  31. hits = models.PositiveIntegerField(default=0)
  32. modified = models.DateTimeField(auto_now=True)
  33. content_type = models.ForeignKey(
  34. ContentType, related_name="content_type_set_for_%(class)s", on_delete=models.CASCADE)
  35. object_pk = models.PositiveIntegerField('object ID')
  36. content_object = GenericForeignKey('content_type', 'object_pk')
  37. objects = HitCountManager()
  38. class Meta:
  39. ordering = ('-hits',)
  40. get_latest_by = "modified"
  41. verbose_name = _("hit count")
  42. verbose_name_plural = _("hit counts")
  43. unique_together = ("content_type", "object_pk")
  44. db_table = "hitcount_hit_count"
  45. def __str__(self):
  46. return '%s' % self.content_object
  47. def increase(self):
  48. self.hits = F('hits') + 1
  49. self.save()
  50. def decrease(self):
  51. self.hits = F('hits') - 1
  52. self.save()
  53. def hits_in_last(self, **kwargs):
  54. """
  55. Returns hit count for an object during a given time period.
  56. This will only work for as long as hits are saved in the Hit database.
  57. If you are purging your database after 45 days, for example, that means
  58. that asking for hits in the last 60 days will return an incorrect
  59. number as that the longest period it can search will be 45 days.
  60. For example: hits_in_last(days=7).
  61. Accepts days, seconds, microseconds, milliseconds, minutes,
  62. hours, and weeks. It's creating a datetime.timedelta object.
  63. """
  64. assert kwargs, "Must provide at least one timedelta arg (eg, days=1)"
  65. period = timezone.now() - timedelta(**kwargs)
  66. return self.hit_set.filter(created__gte=period).count()
  67. @python_2_unicode_compatible
  68. class Hit(models.Model):
  69. """
  70. Model captures a single Hit by a visitor.
  71. None of the fields are editable because they are all dynamically created.
  72. Browsing the Hit list in the Admin will allow one to blacklist both
  73. IP addresses as well as User Agents. Blacklisting simply causes those
  74. hits to not be counted or recorded.
  75. Depending on how long you set the HITCOUNT_KEEP_HIT_ACTIVE, and how long
  76. you want to be able to use `HitCount.hits_in_last(days=30)` you can choose
  77. to clean up your Hit table by using the management `hitcount_cleanup`
  78. management command.
  79. """
  80. created = models.DateTimeField(editable=False, auto_now_add=True, db_index=True)
  81. ip = models.CharField(max_length=40, editable=False, db_index=True)
  82. session = models.CharField(max_length=40, editable=False, db_index=True)
  83. user_agent = models.CharField(max_length=255, editable=False)
  84. user = models.ForeignKey(AUTH_USER_MODEL, null=True, editable=False, on_delete=models.CASCADE)
  85. hitcount = models.ForeignKey(HitCount, editable=False, on_delete=models.CASCADE)
  86. objects = HitManager()
  87. class Meta:
  88. ordering = ('-created',)
  89. get_latest_by = 'created'
  90. verbose_name = _("hit")
  91. verbose_name_plural = _("hits")
  92. def __str__(self):
  93. return 'Hit: %s' % self.pk
  94. def save(self, *args, **kwargs):
  95. """
  96. The first time the object is created and saved, we increment
  97. the associated HitCount object by one. The opposite applies
  98. if the Hit is deleted.
  99. """
  100. if self.pk is None:
  101. self.hitcount.increase()
  102. super(Hit, self).save(*args, **kwargs)
  103. def delete(self, save_hitcount=False):
  104. """
  105. If a Hit is deleted and save_hitcount=True, it will preserve the
  106. HitCount object's total. However, under normal circumstances, a
  107. delete() will trigger a subtraction from the HitCount object's total.
  108. NOTE: This doesn't work at all during a queryset.delete().
  109. """
  110. delete_hit_count.send(
  111. sender=self, instance=self, save_hitcount=save_hitcount)
  112. super(Hit, self).delete()
  113. class HitCountMixin(object):
  114. """
  115. HitCountMixin provides an easy way to add a `hit_count` property to your
  116. model that will return the related HitCount object.
  117. """
  118. @property
  119. def hit_count(self):
  120. ctype = ContentType.objects.get_for_model(self.__class__)
  121. hit_count, created = HitCount.objects.get_or_create(
  122. content_type=ctype, object_pk=self.pk)
  123. return hit_count