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 @@ |
| 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 | 54 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -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 @@ |
| 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 | ... | ... |