Commit 55eab2f1cdaf7b043fe47392f8228031fedf792e
1 parent
c594339b
Exists in
master
and in
39 other branches
Adding missing directory. Updates #21
Showing
7 changed files
with
123 additions
and
0 deletions
Show diff stats
| @@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +import datetime | ||
| 3 | +from south.db import db | ||
| 4 | +from south.v2 import SchemaMigration | ||
| 5 | +from django.db import models | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Migration(SchemaMigration): | ||
| 9 | + | ||
| 10 | + def forwards(self, orm): | ||
| 11 | + # Adding model 'Hit' | ||
| 12 | + db.create_table(u'hitcount_hit', ( | ||
| 13 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
| 14 | + ('created', self.gf('django.db.models.fields.DateField')(auto_now_add=True, db_index=True, blank=True)), | ||
| 15 | + ('updated', self.gf('django.db.models.fields.DateField')(auto_now=True, db_index=True, blank=True)), | ||
| 16 | + ('hits', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), | ||
| 17 | + ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])), | ||
| 18 | + ('object_pk', self.gf('django.db.models.fields.CharField')(max_length=256)), | ||
| 19 | + )) | ||
| 20 | + db.send_create_signal(u'hitcount', ['Hit']) | ||
| 21 | + | ||
| 22 | + # Adding unique constraint on 'Hit', fields ['content_type', 'object_pk'] | ||
| 23 | + db.create_unique(u'hitcount_hit', ['content_type_id', 'object_pk']) | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + def backwards(self, orm): | ||
| 27 | + # Removing unique constraint on 'Hit', fields ['content_type', 'object_pk'] | ||
| 28 | + db.delete_unique(u'hitcount_hit', ['content_type_id', 'object_pk']) | ||
| 29 | + | ||
| 30 | + # Deleting model 'Hit' | ||
| 31 | + db.delete_table(u'hitcount_hit') | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + models = { | ||
| 35 | + u'contenttypes.contenttype': { | ||
| 36 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 37 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 38 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 39 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 40 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| 41 | + }, | ||
| 42 | + u'hitcount.hit': { | ||
| 43 | + 'Meta': {'unique_together': "(('content_type', 'object_pk'),)", 'object_name': 'Hit'}, | ||
| 44 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
| 45 | + 'created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), | ||
| 46 | + 'hits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), | ||
| 47 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 48 | + 'object_pk': ('django.db.models.fields.CharField', [], {'max_length': '256'}), | ||
| 49 | + 'updated': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}) | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + complete_apps = ['hitcount'] | ||
| 0 | \ No newline at end of file | 54 | \ No newline at end of file |
| @@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
| 1 | + | ||
| 2 | +from django.db import models | ||
| 3 | +from django.core.cache import cache | ||
| 4 | +from django.contrib.contenttypes import generic | ||
| 5 | +from django.contrib.contenttypes.models import ContentType | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Hit(models.Model): | ||
| 9 | + created = models.DateField(auto_now_add=True, db_index=True) | ||
| 10 | + updated = models.DateField(auto_now=True, db_index=True) | ||
| 11 | + hits = models.PositiveIntegerField(default=0) | ||
| 12 | + content_type = models.ForeignKey(ContentType) | ||
| 13 | + object_pk = models.CharField(max_length=256) | ||
| 14 | + | ||
| 15 | + class Meta: | ||
| 16 | + unique_together = ('content_type', 'object_pk') | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +class HitCountModelMixin(object): | ||
| 20 | + | ||
| 21 | + @property | ||
| 22 | + def hits(self): | ||
| 23 | + content_type = ContentType.objects.get_for_model(self.__class__, | ||
| 24 | + for_concrete_model=False) | ||
| 25 | + try: | ||
| 26 | + hit = Hit.objects.get(content_type=content_type, | ||
| 27 | + object_pk=self.pk) | ||
| 28 | + except Hit.DoesNotExist: | ||
| 29 | + return 0 | ||
| 30 | + | ||
| 31 | + return hit.hits | ||
| 32 | + | ||
| 33 | + def hit(self, request=None): | ||
| 34 | + content_type = ContentType.objects.get_for_model(self.__class__) | ||
| 35 | + | ||
| 36 | + # Here we cache the user's IP to ensure that the same | ||
| 37 | + # IP won't hit the same page again for while | ||
| 38 | + if request: | ||
| 39 | + ip_addr = request.META.get('REMOTE_ADDR') | ||
| 40 | + cache_key = u'page_hits-{}-{}-{}'.format(ip_addr, | ||
| 41 | + content_type, self.pk) | ||
| 42 | + duplicate = cache.get(cache_key) | ||
| 43 | + if duplicate: | ||
| 44 | + return | ||
| 45 | + cache.set(cache_key, True) | ||
| 46 | + | ||
| 47 | + # Everything ok, so just increment the page count | ||
| 48 | + hit_ = Hit.objects.get_or_create(content_type=content_type, | ||
| 49 | + object_pk=self.pk)[0] | ||
| 50 | + hit_.hits += 1 | ||
| 51 | + hit_.save() |
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +from django.shortcuts import render | ||
| 2 | + | ||
| 3 | +class HitCountViewMixin(object): | ||
| 4 | + def get_object(self): | ||
| 5 | + raise NotImplementedError | ||
| 6 | + | ||
| 7 | + def dispatch(self, request, *args, **kwargs): | ||
| 8 | + response = super(HitCountViewMixin, self).dispatch(request, | ||
| 9 | + *args, **kwargs) | ||
| 10 | + if 200 <= response.status_code < 300: | ||
| 11 | + obj = self.get_object() | ||
| 12 | + if obj: obj.hit(request) | ||
| 13 | + return response |