Skip to content
Browse files

MERGE

  • Loading branch information...
2 parents 7bf65ad + 2e71c67 commit 9cb6af67893b34a3d1fa6d51cdbe345c3aa6f0dd @flashingpumpkin flashingpumpkin committed Jan 5, 2011
View
3 .gitignore
@@ -1 +1,2 @@
-*pyc
+*.pyc
+
View
42 follow/__init__.py
@@ -5,23 +5,19 @@
*************
====================
-Management
+Models
====================
-
-
-.. automodule:: follow.management
+
+.. automodule:: follow.models
:members:
:undoc-members:
- :show-inheritance:
-
+
====================
-Models
+Util
====================
-
-.. automodule:: follow.models
+.. automodule:: follow.util
:members:
:undoc-members:
- :show-inheritance:
====================
Views
@@ -30,37 +26,19 @@
.. automodule:: follow.views
:members:
:undoc-members:
- :show-inheritance:
-
"""
from models import Follow
-
-def follow(user, follower):
- follow, created = Follow.objects.get_or_create(user=user, follower=follower)
- if follow.deleted:
- follow.deleted = False
- follow.save()
+def follow(follower, obj):
+ follow, created = Follow.objects.get_or_create(user=follower, obj=obj)
return follow
-def unfollow(user, follower):
+def unfollow(follower, obj):
try:
- follow = Follow.objects.get(user=user, follower=follower)
+ follow = Follow.objects.get_object(user=follower, obj=obj)
except Follow.DoesNotExist:
return None
follow.delete()
return follow
-
-def block(user, follower, blocked=True):
- try:
- follow = Follow.objects.get(user=user, follower=follower)
- except Follow.DoesNotExist:
- return None
- follow.block_user(blocked)
- follow.save()
- return follow
-
-def unblock (user, follower):
- return block(user, follower, blocked=False)
View
4 follow/admin.py
@@ -0,0 +1,4 @@
+from follow.models import Follow
+from django.contrib import admin
+
+admin.site.register(Follow)
View
17 follow/management.py
@@ -1,17 +0,0 @@
-from django.db.models import signals
-from django.db import connection
-
-try:
- from notification import models as notification
-
- def create_notice_types(app, created_models, verbosity, **kwargs):
- notification.create_notice_type("follow_followed", "New follower", "You were followed by a user")
- notification.create_notice_type("follow_unfollowed", "New unfollower", "You were unfollowed by a user")
- notification.create_notice_type("follow_blocked", "Blocked", "You were blocked by a user")
- notification.create_notice_type("follow_unblocked", "Unblocked", "You were unblocked by a user")
- signals.post_syncdb.connect(create_notice_types, sender = notification)
-
-except ImportError:
- print "Skipping creation of notice types - notification application not found"
-
-
View
197 follow/models.py
@@ -5,110 +5,127 @@
from django.contrib.auth.models import User
from django.conf import settings
-try:
- from notification import models as notification
-except ImportError:
- notification = None
-
-try:
- import stream
-except:
- stream = None
-
-FOLLOW_NOTIFICATION = getattr(settings, 'FOLLOW_NOTIFICATION', False)
+from util import model_map
class FollowManager(models.Manager):
- def get_followers(self, user):
- return self.filter(deleted=False, blocked=False, user=user).select_related()
- def get_followed(self, user):
- return self.filter(deleted=False, blocked=False, follower=user).select_related()
+ def create(self, user, obj, **kwargs):
+ follow = super(FollowManager, self).create(follower=user, **kwargs)
-class Follow(models.Model):
- user = models.ForeignKey(
- User,
- blank=False,
- null=False,
- related_name='followers'
- )
+ rel_name, f_name, m2m = model_map[obj.__class__]
+ if m2m:
+ field = getattr(follow, f_name)
+ field.add(obj)
+ else:
+ setattr(follow, f_name, obj)
+ follow.save()
+ return follow
+
+ def is_user_following(self, user, obj):
+ return user in self.get_followers_for_object(obj)
+
+ def get_or_create(self, user, obj, **kwargs):
+ if not self.is_user_following(user, obj):
+ return self.create(user, obj, **kwargs), True
+
+ return self.get_object(user, obj, **kwargs), False
+
+ def get_object(self, user, obj, **kwargs):
+ rel_name, f_name, m2m = model_map[obj.__class__]
+ kwargs.update({f_name: obj})
+ return self.filter(**kwargs).get(follower=user)
+
+ def get_followers_for_model(self, model):
+ """
+ Usage::
+
+ >>> Follow.objects.get_followers_for_model(Celeb)
+ [<User: devioustree>, <User: flashingpumpkin>]
+
+ """
+ rel_name, f_name, m2m = model_map[model]
+ kwargs = {f_name: None}
+ return User.objects.filter(following__in=self.exclude(**kwargs)).distinct()
+
+ def get_followers_for_object(self, obj):
+ """
+ Usage::
+
+ >>> Follow.objects.get_followers_for_object(celeb)
+ [<User: devioustree>]
+
+ When given an object (of any type but must have been previously registered), it returns a queryset
+ containing all the users following that object
+ """
+ rel_name, f_name, m2m = model_map[obj.__class__]
+ kwargs = {f_name: obj}
+ return User.objects.filter(following__in=self.filter(**kwargs)).distinct()
+
+ def get_models_user_follows(self, user):
+ """
+ Usage::
+
+ >>> Follow.objects.get_models_user_follows(devioustree)
+ [Celeb, Event]
+
+ """
+ model_list = []
+ for model, (rel_name, f_name, m2m) in model_map.iteritems():
+ kwargs = {f_name: None}
+ if Follow.objects.filter(follower=user).exclude(**kwargs):
+ model_list.append(model)
+ return model_list
+
+ def get_objects_user_follows(self, user, models):
+ """
+ Usage::
+
+ >>> Follow.objects.get_objects_user_follows(devioustree, Celeb)
+ [<Follow: Andy Ashburner>]
+ >>> Follow.objects.get_objects_user_follows(devioustree, [Celeb, Event])
+ [<Follow: Andy Ashburner>, <Follow: Oscars>]
+ """
+ kwargs = {}
+ if isinstance(models, list):
+ for model in models:
+ rel_name, f_name, m2m = model_map[model]
+ kwargs[f_name] = None
+ else:
+ rel_name, f_name, m2m = model_map[models]
+ kwargs[f_name] = None
+ return self.exclude(**kwargs).filter(follower=user)
+
+ def get_everything_user_follows(self, user):
+ """
+ Usage::
+
+ >>> Follow.objects.get_everything_user_follows(devioustree)
+ [<Follow: Andy Ashburner>, <Follow: Oscars>]
+
+ """
+ return self.filter(follower=user)
+class Follow(models.Model):
+ """
+ This model allows a user to follow any kind of object
+ """
follower = models.ForeignKey(
User,
blank=False,
null=False,
- related_name='following'
+ related_name='following',
)
datetime = models.DateTimeField(
auto_now_add=True,
)
- deleted = models.BooleanField(
- blank=False,
- null=False,
- default=False,
- )
-
- blocked = models.BooleanField(
- blank=False,
- null=False,
- default=False,
- )
-
- pre_block = None
- block = None
-
objects = FollowManager()
def __unicode__(self):
- return '%s' % self.follower.username
-
- def block_user(self, status):
- self.pre_block = self.blocked
- self.blocked = status
- if self.blocked and not self.pre_block: # New block
- self.block = 'add'
- if not self.blocked and self.pre_block: # Unblock
- self.block = 'del'
- else: # no change
- self.block = None
-
- def delete(self):
- self.deleted = True
- self.save()
-
-def save_handler(sender, instance, created, **kwargs):
- """ Send notifications and register items in the stream :-o """
- if not created: # Handle blocks!
- if instance.block and instance.block == 'new':
- if notification and FOLLOW_NOTIFICATION:
- notification.send([instance.follower], 'follow_blocked', on_site=False)
- if stream and FOLLOW_NOTIFICATION:
- stream.add([instance.user, instance], type='follow_blocked')
- elif instance.block and instance.block == 'del':
- if notification and FOLLOW_NOTIFICATION:
- notification.send([instance.follower], 'follow_unblocked', on_site=False)
- if stream and FOLLOW_NOTIFICATION:
- stream.add([instance.user, instance], type='follow_unblocked')
- else:
- pass
- return
- if notification and FOLLOW_NOTIFICATION:
- notification.send([instance.user], 'follow_followed', on_site=False)
- if stream and FOLLOW_NOTIFICATION:
- stream.add([instance.follower, instance], type='follow_followed')
-
-def delete_handler(sender, instance, **kwargs):
- """ Send notifications and register items in the stream """
- if notification and FOLLOW_NOTIFICATION:
- notification.send([instance.user], 'follow_unfollowed', on_site=False)
- if stream and FOLLOW_NOTIFICATION:
- stream.add([instance.follower, instance], type='follow_unfollowed')
-
-signals.post_save.connect(save_handler, sender=Follow)
-signals.post_save.connect(delete_handler, sender=Follow)
-
-if stream:
- stream.register(Follow)
-
-from django.contrib import admin
-admin.site.register(Follow)
+ return '%s' % self.get_object()
+
+ def get_object(self):
+ for model, (rel_name, f_name, m2m) in model_map.iteritems():
+ if hasattr(self, f_name) and getattr(self, f_name):
+ return getattr(self, f_name)
+
View
0 follow/templates/follow/error.html
No changes.
View
0 follow/templates/follow/success_block.html
No changes.
View
0 follow/templates/follow/success_follow.html
No changes.
View
0 follow/templates/follow/success_unblock.html
No changes.
View
0 follow/templates/follow/success_unfollow.html
No changes.
View
0 follow/templates/stream/follow_blocked.html
No changes.
View
0 follow/templates/stream/follow_followed.html
No changes.
View
0 follow/templates/stream/follow_unblocked.html
No changes.
View
0 ...w/templates/stream/follow_unfollowed.html → follow/templatetags/__init__.py
File renamed without changes.
View
85 follow/templatetags/follow_tags.py
@@ -0,0 +1,85 @@
+from django import template
+from django.core.urlresolvers import reverse
+from follow.models import Follow
+import re
+
+register = template.Library()
+
+def _follow_link(object):
+ return reverse('follow', args=[object._meta.app_label, object._meta.object_name.lower(), object.pk])
+
+def _unfollow_link(object):
+ return reverse('unfollow', args=[object._meta.app_label, object._meta.object_name.lower(), object.pk])
+
+@register.tag
+def follow_url(parser, token):
+ """
+ Returns either a link to follow or to unfollow.
+
+ Usage::
+
+ {% follow_url object %}
+
+ """
+
+ try:
+ tag, obj = token.split_contents()
+ except:
+ raise template.TemplateSyntaxError("The ``follow_link`` tag requires exactly one argument.")
+ return FollowLinkNode(obj)
+
+class FollowLinkNode(template.Node):
+ def __init__(self, obj):
+ self.obj = template.Variable(obj)
+
+ def render(self, context):
+ obj = self.obj.resolve(context)
+
+ try:
+ request = context['request']
+ except KeyError:
+ raise template.TemplateSyntaxError('There is no request object in the template context.')
+
+ if Follow.objects.is_user_following(request.user, obj):
+ return _unfollow_link(obj)
+ else:
+ return _follow_link(obj)
+
+@register.tag
+def follow_class(parser, token):
+ """
+ Checks if the user follows a given object and then returns one of two
+ specified arguments.
+
+ Usage::
+
+ {% follow_class object "followed" "unfollowed" %}
+
+ """
+
+ try:
+ bits = token.split_contents()
+ except:
+ raise template.TemplateSyntaxError("The ``follow_class`` tag requires two or three arguments.")
+ return FollowClassNode(*bits[1:])
+
+class FollowClassNode(template.Node):
+ def __init__(self, obj, followed, unfollowed=''):
+ self.obj = template.Variable(obj)
+
+ self.followed = re.subn('(^("|\')|("|\')$)', '', followed)[0]
+ self.unfollowed = re.subn('(^("|\')|("|\')$)', '', unfollowed)[0]
+
+ def render(self, context):
+ obj = self.obj.resolve(context)
+
+ try:
+ request = context['request']
+ except KeyError:
+ raise template.TemplateSyntaxError('There is no request object in the template context.')
+
+ if Follow.objects.is_user_following(request.user, obj):
+ return self.followed
+ else:
+ return self.unfollowed
+
View
6 follow/urls.py
@@ -1,8 +1,6 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('',
- url('^follow/(?P<username>.*)$', 'follow.views.follow', name = 'follow'),
- url('^unfollow/(?P<username>.*)$', 'follow.views.unfollow', name = 'unfollow'),
- url('^block/(?P<username>.*)$', 'follow.views.block', name = 'block'),
- url('^unblock/(?P<username>.*)$', 'follow.views.unblock', name = 'unblock'),
+ url(r'^follow/(?P<app>.*)/(?P<model>.*)/(?P<id>.*)/$', 'follow.views.follow', name = 'follow'),
+ url(r'^unfollow/(?P<app>.*)/(?P<model>.*)/(?P<id>.*)/$', 'follow.views.unfollow', name = 'unfollow'),
)
View
43 follow/util.py
@@ -0,0 +1,43 @@
+from django.db.models.fields.related import ManyToManyField, ForeignKey
+
+registry = []
+model_map = {}
+
+def followers_for_object(self):
+ from models import Follower
+ return Follow.objects.get_followers_for_object(self)
+
+def register(model, field_name = None, m2m = False):
+ """
+ This registers the model class so it can have followers
+ """
+ from models import Follow
+ if model in registry:
+ return
+
+ registry.append(model)
+
+ related_name = 'follow_%s' % model._meta.module_name
+
+ if not field_name:
+ field_name = model._meta.module_name
+
+ # Create foreignkeys by default - less sql queries for lookups
+ if m2m:
+ field = ManyToManyField(
+ model,
+ related_name = related_name,
+ )
+ else:
+ field = ForeignKey(
+ model,
+ related_name = related_name,
+ blank = True,
+ null = True,
+ )
+
+ field.contribute_to_class(Follow, field_name)
+ setattr(model, 'followers', followers_for_object)
+
+ # We need to keep track of which fields and which kind of fields point where
+ model_map[model] = [related_name, field_name, m2m]
View
78 follow/views.py
@@ -1,65 +1,43 @@
-from follow import follow as _follow, unfollow as _unfollow, \
- block as _block, unblock as _unblock
-
-from django.template import RequestContext
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.db.models.loading import cache
+from django.http import (HttpResponse, HttpResponseBadRequest,
+ HttpResponseRedirect, HttpResponseServerError)
from django.shortcuts import render_to_response
+from django.template import RequestContext
from django.utils.translation import gettext as _
+from follow import follow as _follow, unfollow as _unfollow
-from django.contrib.auth.models import User
def check(func):
"""
Check the permissions, http method and login state
"""
- def iCheck(request, username):
- if not request.user.is_authenticated():
- return render_to_response(
- 'follow/error.html', dict(message=_("Please log in first.")),
- context_instance=RequestContext(request)
- )
-
- if not request.is_ajax():
- return render_to_response(
- 'follow/error.html', dict(message=_("Wrong method.")),
- context_instance=RequestContext(request)
- )
+ def iCheck(request, *args, **kwargs):
+ follow = func(request, *args, **kwargs)
+ if request.is_ajax():
+ return HttpResponse('ok')
try:
- user = User.objects.get(username=username)
- except User.DoesNotExist:
- return render_to_response(
- 'follow/error.html', dict(message=_("Bad Username")),
- context_instance=RequestContext(request)
- )
- if user == request.user:
- return render_to_response(
- 'follow/error.html', dict(message=_("You can't %s yourself." % func.__name__)),
- context_instance=RequestContext(request)
- )
-
- func(request, user)
- return render_to_response(
- 'follow/success_%s.html' % func.__name__, dict(),
- context_instance=RequestContext(request)
- )
+ return HttpResponseRedirect(request.META['HTTP_REFERER'])
+ except KeyError:
+ try:
+ return HttpResponseRedirect(follow.get_object().get_absolute_url())
+ except AttributeError:
+ return HttpResponseServerError('"%s" object of type ``%s`` has no method ``get_absolute_url()``.' % (
+ unicode(follow.get_object()), follow.get_object().__class__))
return iCheck
-@check
-def follow(request, user):
- _follow(user, request.user)
-
-
+@login_required
@check
-def unfollow(request, user):
- _unfollow(user, request.user)
-
+def follow(request, app, model, id):
+ model = cache.get_model(app, model)
+ obj = model.objects.get(pk=id)
+ return _follow(request.user, obj)
+@login_required
@check
-def block(request, user):
- _block(request.user, user)
-
-
-@check
-def unblock(request, user):
- _unblock(request.user, user)
-
+def unfollow(request, app, model, id):
+ model = cache.get_model(app, model)
+ obj = model.objects.get(pk=id)
+ return _unfollow(request.user, obj)

0 comments on commit 9cb6af6

Please sign in to comment.
Something went wrong with that request. Please try again.