david / django-invitation
Built on top of django-registration, it restricts registration to a given number of invited person per active user (strategy introduced by GMail to involve 2.0 users).
Clone this repository (size: 60.6 KB): HTTPS / SSH
$ hg clone http://code.welldev.org/django-invitation
| commit 22: | 8d2a11dc6129 |
| parent 21: | b2977a099938 |
| branch: | default |
Added InvitationUser model to support explicit per-user invitations_remaining.
See issue 4:
http://bitbucket.org/david/django-invitation/issue/4/support-user-specific-number-of
Changed (Δ2.5 KB):
raw changeset »
invitation/admin.py (5 lines added, 1 lines removed)
invitation/models.py (29 lines added, 2 lines removed)
invitation/tests.py (25 lines added, 1 lines removed)
Up to file-list invitation/admin.py:
1 |
1 |
from django.contrib import admin |
2 |
from invitation.models import InvitationKey |
|
2 |
from invitation.models import InvitationKey, InvitationUser |
|
3 |
3 |
|
4 |
4 |
class InvitationKeyAdmin(admin.ModelAdmin): |
5 |
5 |
list_display = ('__unicode__', 'from_user', 'date_invited', 'key_expired') |
6 |
6 |
|
7 |
class InvitationUserAdmin(admin.ModelAdmin): |
|
8 |
list_display = ('inviter', 'invitations_remaining') |
|
9 |
||
7 |
10 |
admin.site.register(InvitationKey, InvitationKeyAdmin) |
11 |
admin.site.register(InvitationUser, InvitationUserAdmin) |
Up to file-list invitation/models.py:
| … | … | @@ -52,8 +52,7 @@ class InvitationKeyManager(models.Manage |
52 |
52 |
""" |
53 |
53 |
Returns the number of remaining invitations for a given ``User``. |
54 |
54 |
""" |
55 |
inviteds_count = self.filter(from_user=user).count() |
|
56 |
return settings.INVITATIONS_PER_USER - inviteds_count |
|
55 |
return InvitationUser.objects.get(inviter=user).invitations_remaining |
|
57 |
56 |
|
58 |
57 |
def delete_expired_keys(self): |
59 |
58 |
for key in self.all(): |
| … | … | @@ -124,3 +123,31 @@ class InvitationKey(models.Model): |
124 |
123 |
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [email]) |
125 |
124 |
|
126 |
125 |
|
126 |
class InvitationUser(models.Model): |
|
127 |
inviter = models.ForeignKey(User, unique=True) |
|
128 |
invitations_remaining = models.IntegerField() |
|
129 |
||
130 |
def __unicode__(self): |
|
131 |
return u"InvitationUser for %s" % self.inviter.username |
|
132 |
||
133 |
||
134 |
def user_post_save(sender, instance, created, **kwargs): |
|
135 |
"""Create InvitationUser for user when User is created.""" |
|
136 |
if created: |
|
137 |
invitation_user = InvitationUser() |
|
138 |
invitation_user.inviter = instance |
|
139 |
invitation_user.invitations_remaining = settings.INVITATIONS_PER_USER |
|
140 |
invitation_user.save() |
|
141 |
||
142 |
models.signals.post_save.connect(user_post_save, sender=User) |
|
143 |
||
144 |
def invitation_key_post_save(sender, instance, created, **kwargs): |
|
145 |
"""Decrement invitations_remaining when InvitationKey is created.""" |
|
146 |
if created: |
|
147 |
invitation_user = InvitationUser.objects.get(inviter=instance.from_user) |
|
148 |
remaining = invitation_user.invitations_remaining |
|
149 |
invitation_user.invitations_remaining = remaining-1 |
|
150 |
invitation_user.save() |
|
151 |
||
152 |
models.signals.post_save.connect(invitation_key_post_save, sender=InvitationKey) |
|
153 |
Up to file-list invitation/tests.py:
| … | … | @@ -27,7 +27,7 @@ from django.core.urlresolvers import rev |
27 |
27 |
from django.test import TestCase |
28 |
28 |
|
29 |
29 |
from invitation import forms |
30 |
from invitation.models import InvitationKey |
|
30 |
from invitation.models import InvitationKey, InvitationUser |
|
31 |
31 |
|
32 |
32 |
class InvitationTestCase(TestCase): |
33 |
33 |
""" |
| … | … | @@ -97,8 +97,32 @@ class InvitationModelTests(InvitationTes |
97 |
97 |
""" |
98 |
98 |
management.call_command('cleanupinvitation') |
99 |
99 |
self.assertEqual(InvitationKey.objects.count(), 1) |
100 |
||
101 |
def test_invitations_remaining(self): |
|
102 |
"""Test InvitationUser calculates remaining invitations properly.""" |
|
103 |
remaining_invites = InvitationKey.objects.remaining_invitations_for_user |
|
100 |
104 |
|
105 |
# New user starts with settings.INVITATIONS_PER_USER |
|
106 |
user = User.objects.create_user(username='newbie', |
|
107 |
password='secret', |
|
108 |
email='newbie@example.com') |
|
109 |
self.assertEqual(remaining_invites(user), settings.INVITATIONS_PER_USER) |
|
101 |
110 |
|
111 |
# After using some, amount remaining is decreased |
|
112 |
used = InvitationKey.objects.filter(from_user=self.sample_user).count() |
|
113 |
expected_remaining = settings.INVITATIONS_PER_USER - used |
|
114 |
remaining = remaining_invites(self.sample_user) |
|
115 |
self.assertEqual(remaining, expected_remaining) |
|
116 |
||
117 |
# Using Invitationuser via Admin, remaining can be increased |
|
118 |
invitation_user = InvitationUser.objects.get(inviter=self.sample_user) |
|
119 |
new_remaining = 2*settings.INVITATIONS_PER_USER + 1 |
|
120 |
invitation_user.invitations_remaining = new_remaining |
|
121 |
invitation_user.save() |
|
122 |
remaining = remaining_invites(self.sample_user) |
|
123 |
self.assertEqual(remaining, new_remaining) |
|
124 |
||
125 |
||
102 |
126 |
class InvitationFormTests(InvitationTestCase): |
103 |
127 |
""" |
104 |
128 |
Tests for the forms and custom validation logic included in |
