Skip to content

Commit 9efcdf1

Browse files
committed
Add a mixin class for dashboard views on models with project relations
As we overhaul the project admin dashboard, a good amount of code can be cleaned up by using a CBV rather than repeating the view code like is currently used. I had a need for this outside this code base, but implemented it here instead. Views would look like: class DomainMixin(ProjectRelationMixin): model = Domain lookup_url_kwarg = 'pk' class ListDomainView(DomainMixin, ListView): pass class DetailDomainView(DomainMixin, DetailView): pass Views would then have access to a `project` and `domains` in template context data.
1 parent 7963d07 commit 9efcdf1

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

readthedocs/projects/views/mixins.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Mixin classes for project views"""
2+
3+
from django.shortcuts import get_object_or_404
4+
5+
from readthedocs.projects.models import Project
6+
7+
8+
class ProjectRelationMixin(object):
9+
10+
"""Mixin class for constructing model views for project dashboard
11+
12+
This mixin class is used for model views on models that have a relation
13+
to the :py:cls:`Project` model.
14+
15+
:cvar project_lookup_url_kwarg: URL kwarg to use in project lookup
16+
:cvar project_lookup_field: Query field for project relation
17+
:cvar project_context_object_name: Context object name for project
18+
"""
19+
20+
project_lookup_url_kwarg = 'project_slug'
21+
project_lookup_field = 'project'
22+
project_context_object_name = 'project'
23+
24+
def get_project_queryset(self):
25+
return Project.objects.for_admin_user(user=self.request.user)
26+
27+
def get_project(self):
28+
if self.project_lookup_url_kwarg not in self.kwargs:
29+
return None
30+
return get_object_or_404(
31+
self.get_project_queryset(),
32+
slug=self.kwargs[self.project_lookup_url_kwarg]
33+
)
34+
35+
def get_queryset(self):
36+
return self.model.objects.filter(
37+
**{self.project_lookup_field: self.get_project()}
38+
)
39+
40+
def get_context_data(self, **kwargs):
41+
context = {}
42+
try:
43+
context = super(ProjectRelationMixin, self).get_context_data(**kwargs)
44+
except AttributeError:
45+
pass
46+
context[self.project_context_object_name] = self.get_project()
47+
return context

readthedocs/rtd_tests/tests/test_project_views.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
from readthedocs.rtd_tests.base import (WizardTestCase, MockBuildTestCase,
1212
RequestFactoryTestMixin)
1313
from readthedocs.projects.exceptions import ProjectSpamError
14-
from readthedocs.projects.models import Project
14+
from readthedocs.projects.models import Project, Domain
1515
from readthedocs.projects.views.private import ImportWizardView
16+
from readthedocs.projects.views.mixins import ProjectRelationMixin
1617

1718

1819
@patch('readthedocs.projects.views.private.trigger_build', lambda x, basic: None)
@@ -330,3 +331,27 @@ def test_delete_project(self):
330331
remove_dir.apply_async.assert_called_with(
331332
queue='celery',
332333
args=[project.doc_path])
334+
335+
336+
class TestPrivateMixins(MockBuildTestCase):
337+
338+
def setUp(self):
339+
self.project = get(Project, slug='kong')
340+
self.domain = get(Domain, project=self.project)
341+
342+
def test_project_relation(self):
343+
"""Class using project relation mixin class"""
344+
345+
class FoobarView(ProjectRelationMixin):
346+
model = Domain
347+
348+
def get_project_queryset(self):
349+
# Don't test this as a view with a request.user
350+
return Project.objects.all()
351+
352+
353+
view = FoobarView()
354+
view.kwargs = {'project_slug': 'kong'}
355+
self.assertEqual(view.get_project(), self.project)
356+
self.assertEqual(view.get_queryset().first(), self.domain)
357+
self.assertEqual(view.get_context_data(), {'project': self.project})

0 commit comments

Comments
 (0)