56π
I am not sure if itβs in the best taste to answer my own question but, after a bit of work, I put together an app that solves the problems in earnest: django-hitcount.
You can read about how to use it at the documentation page.
The ideas for django-hitcount came came from both of my two original answers (Teebes -and- vikingosegundo), which really got me started thinking about the whole thing.
This is my first attempt at sharing a pluggable app with the community and hope someone else finds it useful. Thanks!
33π
You should use the django built-in session framework, it already does a lot of this for you. I implemented this in the following way with a Q&A app where I wanted to track views:
in models.py:
class QuestionView(models.Model):
question = models.ForeignKey(Question, related_name='questionviews', on_delete=models.CASCADE)
ip = models.CharField(max_length=40)
session = models.CharField(max_length=40)
created = models.DateTimeField(default=datetime.datetime.now())
in views.py:
def record_view(request, question_id):
question = get_object_or_404(Question, pk=question_id)
if not QuestionView.objects.filter(
question=question,
session=request.session.session_key):
view = QuestionView(question=question,
ip=request.META['REMOTE_ADDR'],
created=datetime.datetime.now(),
session=request.session.session_key)
view.save()
return HttpResponse(u"%s" % QuestionView.objects.filter(question=question).count())
Vikingosegundo is probably right though that using content-type is probably the more reusable solution but definitely donβt reinvent the wheel in terms of tracking sessions, Django already does that!
Last thing, you should probably have the view that records the hit be either called via Ajax or a css link so that search engines donβt rev up your counts.
Hope that helps!
- [Django]-How do you Serialize the User model in Django Rest Framework
- [Django]-How do I use pagination with Django class based generic ListViews?
- [Django]-Distributed task queues (Ex. Celery) vs crontab scripts
14π
You could create a generic Hit model
class Hit(models.Model):
date = models.DateTimeField(auto_now=True)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
In your view.py you write this function:
def render_to_response_hit_count(request,template_path,keys,response):
for key in keys:
for i in response[key]:
Hit(content_object=i).save()
return render_to_response(template_path, response)
and the views that you are interested in return
return render_to_response_hit_count(request, 'map/list.html',['list',],
{
'list': l,
})
This approach gives you the power, not only to count the hit, but to filter the hit-history by time, contenttype and so onβ¦
As the hit-table might be growing fast, you should think about a deletion strategy.
- [Django]-CharField with fixed length, how?
- [Django]-Django models: default value for column
- [Django]-How to force application version on AWS Elastic Beanstalk
9π
I know this question is an old one and also thornomad has put an app to solve the problem and inspire me with me solution. I would like to share this solution since I didnβt find much information about this topic and it may help someone else.
My approach is to make a generic model can be used with any view based on the view path (url).
models.py
class UrlHit(models.Model):
url = models.URLField()
hits = models.PositiveIntegerField(default=0)
def __str__(self):
return str(self.url)
def increase(self):
self.hits += 1
self.save()
class HitCount(models.Model):
url_hit = models.ForeignKey(UrlHit, editable=False, on_delete=models.CASCADE)
ip = models.CharField(max_length=40)
session = models.CharField(max_length=40)
date = models.DateTimeField(auto_now=True)
views.py
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def hit_count(request):
if not request.session.session_key:
request.session.save()
s_key = request.session.session_key
ip = get_client_ip(request)
url, url_created = UrlHit.objects.get_or_create(url=request.path)
if url_created:
track, created = HitCount.objects.get_or_create(url_hit=url, ip=ip, session=s_key)
if created:
url.increase()
request.session[ip] = ip
request.session[request.path] = request.path
else:
if ip and request.path not in request.session:
track, created = HitCount.objects.get_or_create(url_hit=url, ip=ip, session=s_key)
if created:
url.increase()
request.session[ip] = ip
request.session[request.path] = request.path
return url.hits
- [Django]-Actions triggered by field change in Django
- [Django]-How to get the currently logged in user's id in Django?
- [Django]-Can WordPress be replaced by a Framework like Django or Ruby on Rails?
5π
I did this by creating a model PageViews and making a column βHitsβ in it. Every time when Homepage url is hit. I increment the first and only row of column Hit and render it to the template. Here how it looks.
Views.py
def Home(request):
if(PageView.objects.count()<=0):
x=PageView.objects.create()
x.save()
else:
x=PageView.objects.all()[0]
x.hits=x.hits+1
x.save()
context={'page':x.hits}
return render(request,'home.html',context=context)
Models.py
class PageView(models.Model):
hits=models.IntegerField(default=0)
- [Django]-Limit number of characters with Django Template filter
- [Django]-How to implement FirebaseDB with a Django Web Application
- [Django]-How to use python2.7 pip instead of default pip
3π
I did it using cookies. Donβt know if itβs a good idea to do that or not. The following code looks for an already set cookie first if it exists it increases the total_view counter if it is not there the it increases both total_views and unique_views. Both total_views and unique_views are a field of a Django model.
def view(request):
...
cookie_state = request.COOKIES.get('viewed_post_%s' % post_name_slug)
response = render_to_response('community/post.html',context_instance=RequestContext(request, context_dict))
if cookie_state:
Post.objects.filter(id=post.id).update(total_views=F('total_views') + 1)
else:
Post.objects.filter(id=post.id).update(unique_views=F('unique_views') + 1)
Post.objects.filter(id=post.id).update(total_views=F('total_views') + 1)
response.set_cookie('viewed_post_%s' % post_name_slug , True, max_age=2678400)
return response
- [Django]-Difference between reverse() and reverse_lazy() in Django
- [Django]-"Failed building wheel for psycopg2" β MacOSX using virtualenv and pip
- [Django]-Testing email sending in Django