Admin Section API (#11)

Models for managing admin pages
This commit is contained in:
Matthew Ryan Dillon 2016-09-05 17:03:01 -07:00 committed by GitHub
parent 8e08cab3c4
commit 910d3b4beb
15 changed files with 310 additions and 36 deletions

View file

@ -3,11 +3,13 @@ from django.conf.urls import url, include
from rest_framework import routers
from . import views as api_v
from ..utils import viewsets as utils_viewsets
router = routers.DefaultRouter()
router.register(r'administrative-urls', api_v.AdminURLs, base_name='adminurls')
router.register(r'admin-sections', utils_viewsets.AdminSectionViewSet)
router.register(r'admin-entries', utils_viewsets.AdminEntryViewSet)
urlpatterns = [
url(r'^auth/login/', api_v.Login.as_view()),

View file

@ -1,12 +1,8 @@
from collections import OrderedDict
from django.contrib.auth import user_logged_in
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.viewsets import ViewSet
from djoser.views import (LoginView, PasswordResetView,
PasswordResetConfirmView)
@ -39,31 +35,3 @@ class PasswordResetConfirm(PasswordResetConfirmView):
response = super(PasswordResetConfirm, self).action(serializer)
response.data = {}
return response
class AdminURLs(ViewSet):
def get_view_name(self):
return 'Admin URLs List'
def list(self, request, *args, **kwargs):
urls = OrderedDict([
('projects', [
['project', 'projects', 'project'],
['grant', 'projects', 'grant'],
['grant-report', 'projects', 'grantreport'],
]),
('collections', [
['collection-type', 'collections_ccdb', 'collectiontype'],
]),
])
data = OrderedDict()
for category, group in urls.items():
paths = []
for url in group:
lookup = 'admin:{}_{}_changelist'.format(url[1], url[2])
path = reverse(lookup, request=request)
paths.append({'id': url[0], 'url': path})
data[category] = paths
return Response(data)

7
ccdb/utils/admin.py Normal file
View file

@ -0,0 +1,7 @@
from django.contrib import admin
from .models import AdminSection, AdminEntry
admin.site.register(AdminSection)
admin.site.register(AdminEntry)

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-29 04:00
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AdminEntry',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('package', models.CharField(max_length=255)),
('model', models.CharField(max_length=255)),
('sort', models.IntegerField()),
],
),
migrations.CreateModel(
name='AdminSection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('sort', models.IntegerField()),
],
),
migrations.AddField(
model_name='adminentry',
name='section',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.AdminSection'),
),
]

View file

@ -0,0 +1,72 @@
from collections import namedtuple
from django.db import migrations
Section = namedtuple('Section', ['name', 'package', 'entries'])
class Migration(migrations.Migration):
def migrate(apps, schema_editor):
AdminSection = apps.get_model('utils', 'AdminSection')
AdminEntry = apps.get_model('utils', 'AdminEntry')
# Clean up any old stuff
for model in [AdminEntry, AdminSection]:
model.objects.all().delete()
data = [
Section('Collections', 'collections_ccdb', [
'collectiontype', 'collectionmethod', 'flaw', 'adfgpermit',
'datasheetattachment', 'collectiontrap', 'collection'
]),
Section('Experiments', 'experiments', [
'flaw', 'experiment', 'protocolattachment', 'treatmenttype',
'treatment', 'treatmentreplicate', 'alivedeadcount'
]),
Section('Locations', 'locations', [
'region', 'site', 'municipallocation', 'studylocation',
'storagelocation'
]),
Section('Misc.', 'misc', [
'measurementunit', 'measurementtype', 'container', 'material',
'color'
]),
Section('Processing', 'processing', [
'processtype', 'reagent', 'flaw'
]),
Section('Projects', 'projects', [
'project', 'grant', 'grantreport'
]),
Section('Species', 'species', [
'species', 'trapspecies', 'collectionspecies'
]),
Section('Users', 'users', [
'user'
]),
Section('Utils', 'utils', [
'adminsection', 'adminentry'
]),
]
for i, adminsection in enumerate(data):
section = AdminSection.objects.create(name=adminsection.name,
sort=i)
for j, entry in enumerate(adminsection.entries):
AdminEntry.objects.create(package=adminsection.package,
model=entry, section=section, sort=j)
def rollback(apps, schema_editor):
AdminSection = apps.get_model('utils', 'AdminSection')
AdminEntry = apps.get_model('utils', 'AdminEntry')
for model in [AdminEntry, AdminSection]:
model.objects.all().delete()
dependencies = [
('utils', '0001_initial'),
]
operations = [
migrations.RunPython(migrate, rollback),
]

26
ccdb/utils/models.py Normal file
View file

@ -0,0 +1,26 @@
from django.db import models
from rest_framework.reverse import reverse
class AdminSection(models.Model):
name = models.CharField(max_length=255)
sort = models.IntegerField()
def __str__(self):
return self.name
class AdminEntry(models.Model):
package = models.CharField(max_length=255)
model = models.CharField(max_length=255)
section = models.ForeignKey(AdminSection)
sort = models.IntegerField()
def admin_url(self, request=None):
lookup = 'admin:{}_{}_changelist'.format(self.package, self.model)
relative = reverse(lookup)
return request.build_absolute_uri(relative)
def __str__(self):
return "%s %s" % (self.package, self.model)

20
ccdb/utils/serializers.py Normal file
View file

@ -0,0 +1,20 @@
from rest_framework import serializers
from .models import AdminSection, AdminEntry
class AdminSectionSerializer(serializers.ModelSerializer):
class Meta:
model = AdminSection
fields = ('id', 'name', 'sort')
class AdminEntrySerializer(serializers.ModelSerializer):
class Meta:
model = AdminEntry
fields = ('id', 'admin_url', 'package', 'model', 'section', 'sort')
admin_url = serializers.SerializerMethodField()
def get_admin_url(self, obj):
return obj.admin_url(self.context.get('request'))

View file

View file

@ -0,0 +1,21 @@
from factory import DjangoModelFactory, Sequence, SubFactory
from ..models import AdminSection, AdminEntry
class AdminSectionFactory(DjangoModelFactory):
class Meta:
model = AdminSection
name = Sequence(lambda n: 'section{}'.format(n))
sort = Sequence(lambda n: n)
class AdminEntryFactory(DjangoModelFactory):
class Meta:
model = AdminEntry
package = Sequence(lambda n: 'package{}'.format(n))
model = Sequence(lambda n: 'section{}'.format(n))
section = SubFactory(AdminSectionFactory)
sort = Sequence(lambda n: n)

View file

@ -0,0 +1,26 @@
from django.test import TestCase
from django.test.client import RequestFactory
from ..models import AdminSection, AdminEntry
from .factories import AdminSectionFactory, AdminEntryFactory
class AdminSectionTestCase(TestCase):
def test_creation(self):
a = AdminSectionFactory()
self.assertTrue(isinstance(a, AdminSection))
self.assertEqual(a.__str__(), a.name)
class AdminEntryTestCase(TestCase):
def test_creation(self):
a = AdminEntryFactory()
self.assertTrue(isinstance(a, AdminEntry))
self.assertEqual(a.__str__(), "%s %s" % (a.package, a.model))
def test_admin_url(self):
a = AdminEntryFactory(package='utils', model='adminentry')
request_factory = RequestFactory()
request = request_factory.get('/')
self.assertEqual('http://testserver/admin/utils/adminentry/',
a.admin_url(request))

View file

@ -0,0 +1,25 @@
from django.test import TestCase
from django.test.client import RequestFactory
from .factories import AdminSectionFactory, AdminEntryFactory
from ..serializers import AdminSectionSerializer, AdminEntrySerializer
class AdminSectionTestCase(TestCase):
def test_creation(self):
a = AdminSectionFactory()
serializer = AdminSectionSerializer(a)
data = {'name': a.name, 'id': a.id, 'sort': a.sort}
self.assertEqual(data, serializer.data)
class AdminEntryTestCase(TestCase):
def test_creation(self):
a = AdminEntryFactory(package='utils', model='adminentry')
request_factory = RequestFactory()
request = request_factory.get('/')
serializer = AdminEntrySerializer(a, context={'request': request})
data = {'package': a.package, 'id': a.id, 'sort': a.sort,
'model': a.model, 'section': a.section.id,
'admin_url': a.admin_url(request)}
self.assertEqual(data, serializer.data)

View file

@ -0,0 +1,37 @@
from django.core.urlresolvers import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework.authtoken.models import Token
from .factories import AdminSectionFactory, AdminEntryFactory
from ccdb.users.models import User
class AdminTestCase(APITestCase):
def setUp(self):
self.username = 'abc'
self.password = '123'
self.user = User.objects.create(username=self.username,
password=self.password)
self.token = Token.objects.create(user=self.user)
self.client.login(username=self.username, password=self.password)
self.client.credentials(HTTP_AUTHORIZATION='Token %s' % self.token.key)
class AdminSectionTestCase(AdminTestCase):
def test_list(self):
AdminSectionFactory.create_batch(5)
url = reverse('api:v1:adminsection-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 5)
class AdminEntryTestCase(AdminTestCase):
def test_list(self):
AdminEntryFactory.create_batch(5, package='utils', model='adminentry')
url = reverse('api:v1:adminentry-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 5)

17
ccdb/utils/viewsets.py Normal file
View file

@ -0,0 +1,17 @@
from rest_framework import viewsets
from .models import AdminSection, AdminEntry
from .serializers import AdminSectionSerializer, AdminEntrySerializer
class AdminSectionViewSet(viewsets.ModelViewSet):
queryset = AdminSection.objects.all()
serializer_class = AdminSectionSerializer
class AdminEntryViewSet(viewsets.ModelViewSet):
queryset = AdminEntry.objects.all()
serializer_class = AdminEntrySerializer
def get_serializer_context(self):
return {'request': self.request}

View file

@ -196,9 +196,23 @@ REST_FRAMEWORK = {
],
'DEFAULT_VERSIONING_CLASS':
'rest_framework.versioning.NamespaceVersioning',
'DEFAULT_PAGINATION_CLASS':
'drf_ember_pagination.EmberPageNumberPagination',
'PAGE_SIZE': 100,
'EXCEPTION_HANDLER':
'rest_framework_json_api.exceptions.exception_handler',
'DEFAULT_PAGINATION_CLASS':
'rest_framework_json_api.pagination.PageNumberPagination',
'DEFAULT_PARSER_CLASSES': (
'rest_framework_json_api.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_json_api.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_METADATA_CLASS':
'rest_framework_json_api.metadata.JSONAPIMetadata',
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
}
SITE_ID = 1

View file

@ -21,4 +21,4 @@ djangorestframework==3.4.4
django-cors-headers==1.1.0
django-filter==0.14.0
djoser==0.5.0
git+git://github.com/thermokarst/drf_ember_pagination.git
djangorestframework-jsonapi==2.1.0