Merge lp:~salgado/django-group-access/delegate-to-parent-relation into lp:django-group-access

Proposed by Guilherme Salgado
Status: Merged
Merge reported by: Nicola Heald
Merged at revision: not available
Proposed branch: lp:~salgado/django-group-access/delegate-to-parent-relation
Merge into: lp:django-group-access
Diff against target: 179 lines (+114/-5)
3 files modified
models.py (+8/-4)
sandbox/models.py (+35/-1)
tests.py (+71/-0)
To merge this branch: bzr merge lp:~salgado/django-group-access/delegate-to-parent-relation
Reviewer Review Type Date Requested Status
Nicola Heald Approve
Review via email: mp+76042@code.launchpad.net

Description of the change

This makes it possible to use access_relation (former access_child_relation) to do the access-group checking on a parent as well as a child relation.

I've also removed a .filter() call which seemed redundant in accessible_by_user().

To post a comment you must log in.
5. By Guilherme Salgado

Revert my removal of the (seemingly redundant) .filter() call and add a comment explaining why it's there

Revision history for this message
Nicola Heald (notnownikki) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'models.py'
--- models.py 2011-09-13 13:50:21 +0000
+++ models.py 2011-09-19 15:07:26 +0000
@@ -11,18 +11,22 @@
11 def accessible_by_user(self, user):11 def accessible_by_user(self, user):
12 if AccessGroup.objects.filter(members=user, supergroup=True).count():12 if AccessGroup.objects.filter(members=user, supergroup=True).count():
13 return self.all()13 return self.all()
14 if hasattr(self.model, 'access_child_relation'):14 if hasattr(self.model, 'access_relation'):
15 acr = getattr(self.model, 'access_child_relation')15 acr = getattr(self.model, 'access_relation')
16 k = '%s__access_groups__in' % acr16 k = '%s__access_groups__in' % acr
17 access_groups_dict = {k: AccessGroup.objects.filter(members=user)}17 access_groups_dict = {k: AccessGroup.objects.filter(members=user)}
18 k = '%s__isnull' % acr18 k = '%s__isnull' % acr
19 no_child_records = {k: True}19 no_related_records = {k: True}
20 k = '%s__owner' % acr20 k = '%s__owner' % acr
21 direct_owner_dict = {k: user}21 direct_owner_dict = {k: user}
22 available = self.filter(22 available = self.filter(
23 models.Q(**access_groups_dict) |23 models.Q(**access_groups_dict) |
24 models.Q(**direct_owner_dict) |24 models.Q(**direct_owner_dict) |
25 models.Q(**no_child_records)).distinct()25 models.Q(**no_related_records)).distinct()
26 # Although this extra .filter() call seems redundant it turns out
27 # to be a huge performance optimization. Without it the ORM will
28 # join on the related tables and .distinct() them, which killed
29 # performance in HEXR leading to 30+ seconds to load a page.
26 return self.filter(pk__in=available)30 return self.filter(pk__in=available)
27 else:31 else:
28 available = self.filter(32 available = self.filter(
2933
=== modified file 'sandbox/models.py'
--- sandbox/models.py 2011-09-16 18:17:01 +0000
+++ sandbox/models.py 2011-09-19 15:07:26 +0000
@@ -9,7 +9,7 @@
99
10class AccessRestrictedParent(models.Model):10class AccessRestrictedParent(models.Model):
11 name = models.CharField(max_length = 64)11 name = models.CharField(max_length = 64)
12 access_child_relation = 'accessrestrictedmodel'12 access_relation = 'accessrestrictedmodel'
13 13
14 objects = AccessManager()14 objects = AccessManager()
15 15
@@ -33,4 +33,38 @@
33 def __unicode__(self):33 def __unicode__(self):
34 return self.name34 return self.name
3535
36
37class Project(AccessGroupMixin):
38 owner = models.ForeignKey(User, null=True)
39 name = models.CharField(max_length = 64)
40 objects = AccessManager()
41
42 def __unicode__(self):
43 return self.name
44
45
46class Build(AccessGroupMixin):
47 owner = models.ForeignKey(User, null=True)
48 name = models.CharField(max_length = 64)
49 project = models.ForeignKey(Project)
50 access_relation = 'project'
51 objects = AccessManager()
52
53 def __unicode__(self):
54 return self.name
55
56
57class Release(models.Model):
58 owner = models.ForeignKey(User, null=True)
59 name = models.CharField(max_length = 64)
60 build = models.ForeignKey(Build)
61 # We don't have a direct reference to the parent which is
62 # accessgroup-controlled, so we have to go via Build.
63 access_relation = 'build__project'
64 objects = AccessManager()
65
66 def __unicode__(self):
67 return self.name
68
69
36post_save.connect(process_auto_share_groups, AccessRestrictedModel)70post_save.connect(process_auto_share_groups, AccessRestrictedModel)
3771
=== modified file 'tests.py'
--- tests.py 2011-09-16 18:17:01 +0000
+++ tests.py 2011-09-19 15:07:26 +0000
@@ -1,3 +1,5 @@
1import itertools
2
1from django.test import TestCase3from django.test import TestCase
2from django.conf import settings4from django.conf import settings
3from django.core.management import call_command5from django.core.management import call_command
@@ -6,6 +8,9 @@
6from django_group_access.sandbox.models import (8from django_group_access.sandbox.models import (
7 AccessRestrictedModel,9 AccessRestrictedModel,
8 AccessRestrictedParent,10 AccessRestrictedParent,
11 Build,
12 Project,
13 Release,
9)14)
10from django_group_access.models import AccessGroup, Invitation15from django_group_access.models import AccessGroup, Invitation
1116
@@ -31,6 +36,64 @@
31 loading.cache.loaded = False36 loading.cache.loaded = False
3237
3338
39class AccessRelationTests(SyncingTestCase):
40
41 def setUp(self):
42 super(AccessRelationTests, self).setUp()
43 self.owner = _create_user()
44 self.project = Project(owner=self.owner, name='project')
45 self.project.save()
46 self.build = Build(
47 owner=self.owner, name='build', project=self.project)
48 self.build.save()
49 self.release = Release(
50 owner=self.owner, name='release', build=self.build)
51 self.release.save()
52 group = self._create_access_group_with_one_member()
53 self.project.access_groups.add(group)
54 self.project.save()
55 self.user_with_access = group.members.all()[0]
56 self.user_without_access = _create_user()
57
58 def _create_access_group_with_one_member(self):
59 g = AccessGroup(name='oem')
60 g.save()
61 g.members.add(_create_user())
62 g.save()
63 return g
64
65 def test_direct_reference(self):
66 # Build has a foreign key to Project so its access_relation just need
67 # to point to project and we'll take advantage of Django's ORM to do
68 # the access group checks on Project.
69 query_method = Build.objects.accessible_by_user
70
71 self.assertEqual('project', Build.access_relation)
72 self.assertEqual(
73 [self.build.name], [b.name for b in query_method(self.owner)])
74 self.assertEqual(
75 [self.build.name],
76 [b.name for b in query_method(self.user_with_access)])
77 self.assertEqual(
78 [], [b for b in query_method(self.user_without_access)])
79
80 def test_indirect_reference(self):
81 # Release has no foreign key to Project, but it has one to Build
82 # and it can use that to tell us to do the access group checks on
83 # Project.
84 query_method = Release.objects.accessible_by_user
85
86 self.assertEqual('build__project', Release.access_relation)
87 self.assertEqual(
88 [self.release.name], [r.name for r in query_method(self.owner)])
89 self.assertEqual(
90 [self.release.name],
91 [r.name for r in query_method(self.user_with_access)])
92 self.assertEqual(
93 [], [r for r in query_method(self.user_without_access)])
94
95
96
34class InvitationTest(TestCase):97class InvitationTest(TestCase):
3598
36 def setUp(self):99 def setUp(self):
@@ -238,3 +301,11 @@
238 self.assertEqual(available[6].name, 'the cabal record 2')301 self.assertEqual(available[6].name, 'the cabal record 2')
239 self.assertEqual(available[7].name, 'the cabal record extra')302 self.assertEqual(available[7].name, 'the cabal record extra')
240 self.assertEqual(available[8].name, 'the stonecutters record 1')303 self.assertEqual(available[8].name, 'the stonecutters record 1')
304
305
306counter = itertools.count()
307def _create_user():
308 random_string = 'asdfg%d' % counter.next()
309 user = User.objects.create_user(
310 random_string, '%s@example.com' % random_string)
311 return user

Subscribers

People subscribed via source and target branches