Add a django-rest-framework APIView that supports reading/writing the current value of a configuration model
This commit is contained in:
committed by
Peter Fogg
parent
93656e39ee
commit
bf493fffa3
@@ -93,6 +93,8 @@ class ConfigurationModel(models.Model):
|
||||
"""
|
||||
Clear the cached value when saving a new configuration entry
|
||||
"""
|
||||
# Always create a new entry, instead of updating an existing model
|
||||
self.pk = None
|
||||
super(ConfigurationModel, self).save(*args, **kwargs)
|
||||
cache.delete(self.cache_key_name(*[getattr(self, key) for key in self.KEY_FIELDS]))
|
||||
if self.KEY_FIELDS:
|
||||
|
||||
@@ -7,10 +7,13 @@ import ddt
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from mock import patch
|
||||
from mock import patch, Mock
|
||||
from config_models.models import ConfigurationModel
|
||||
from config_models.views import ConfigurationModelCurrentAPIView
|
||||
|
||||
|
||||
class ExampleConfig(ConfigurationModel):
|
||||
@@ -92,6 +95,14 @@ class ConfigurationModelTests(TestCase):
|
||||
self.assertEqual(rows[1].string_field, 'first')
|
||||
self.assertEqual(rows[1].is_active, False)
|
||||
|
||||
def test_always_insert(self, mock_cache):
|
||||
config = ExampleConfig(changed_by=self.user, string_field='first')
|
||||
config.save()
|
||||
config.string_field = 'second'
|
||||
config.save()
|
||||
|
||||
self.assertEquals(2, ExampleConfig.objects.all().count())
|
||||
|
||||
|
||||
class ExampleKeyedConfig(ConfigurationModel):
|
||||
"""
|
||||
@@ -282,3 +293,81 @@ class KeyedConfigurationModelTests(TestCase):
|
||||
fake_result = [('a', 'b'), ('c', 'd')]
|
||||
mock_cache.get.return_value = fake_result
|
||||
self.assertEquals(ExampleKeyedConfig.key_values(), fake_result)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ConfigurationModelAPITests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
self.user = User.objects.create_user(
|
||||
username='test_user',
|
||||
email='test_user@example.com',
|
||||
password='test_pass',
|
||||
)
|
||||
self.user.is_superuser = True
|
||||
self.user.save()
|
||||
|
||||
self.current_view = ConfigurationModelCurrentAPIView.as_view(model=ExampleConfig)
|
||||
|
||||
# Disable caching while testing the API
|
||||
patcher = patch('config_models.models.cache', Mock(get=Mock(return_value=None)))
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_insert(self):
|
||||
self.assertEquals("", ExampleConfig.current().string_field)
|
||||
|
||||
request = self.factory.post('/config/ExampleConfig', {"string_field": "string_value"})
|
||||
request.user = self.user
|
||||
response = self.current_view(request)
|
||||
|
||||
self.assertEquals("string_value", ExampleConfig.current().string_field)
|
||||
self.assertEquals(self.user, ExampleConfig.current().changed_by)
|
||||
|
||||
def test_multiple_inserts(self):
|
||||
for i in xrange(3):
|
||||
self.assertEquals(i, ExampleConfig.objects.all().count())
|
||||
|
||||
request = self.factory.post('/config/ExampleConfig', {"string_field": str(i)})
|
||||
request.user = self.user
|
||||
response = self.current_view(request)
|
||||
self.assertEquals(201, response.status_code)
|
||||
|
||||
self.assertEquals(i+1, ExampleConfig.objects.all().count())
|
||||
self.assertEquals(str(i), ExampleConfig.current().string_field)
|
||||
|
||||
def test_get_current(self):
|
||||
request = self.factory.get('/config/ExampleConfig')
|
||||
request.user = self.user
|
||||
response = self.current_view(request)
|
||||
self.assertEquals('', response.data['string_field'])
|
||||
self.assertEquals(10, response.data['int_field'])
|
||||
self.assertEquals(None, response.data['changed_by'])
|
||||
self.assertEquals(False, response.data['enabled'])
|
||||
self.assertEquals(None, response.data['change_date'])
|
||||
|
||||
ExampleConfig(string_field='string_value', int_field=20).save()
|
||||
|
||||
response = self.current_view(request)
|
||||
self.assertEquals('string_value', response.data['string_field'])
|
||||
self.assertEquals(20, response.data['int_field'])
|
||||
|
||||
@ddt.data(
|
||||
('get', [], 200),
|
||||
('post', [{'string_field': 'string_value', 'int_field': 10}], 201),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_permissions(self, method, args, status_code):
|
||||
request = getattr(self.factory, method)('/config/ExampleConfig', *args)
|
||||
|
||||
request.user = User.objects.create_user(
|
||||
username='no-perms',
|
||||
email='no-perms@example.com',
|
||||
password='no-perms',
|
||||
)
|
||||
response = self.current_view(request)
|
||||
self.assertEquals(403, response.status_code)
|
||||
|
||||
request.user = self.user
|
||||
response = self.current_view(request)
|
||||
self.assertEquals(status_code, response.status_code)
|
||||
|
||||
44
common/djangoapps/config_models/views.py
Normal file
44
common/djangoapps/config_models/views.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from rest_framework.generics import CreateAPIView, RetrieveAPIView
|
||||
from rest_framework.permissions import DjangoModelPermissions
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
|
||||
class ReadableOnlyByAuthors(DjangoModelPermissions):
|
||||
perms_map = DjangoModelPermissions.perms_map.copy()
|
||||
perms_map['GET'] = perms_map['OPTIONS'] = perms_map['HEAD'] = perms_map['POST']
|
||||
|
||||
|
||||
class ConfigurationModelCurrentAPIView(CreateAPIView, RetrieveAPIView):
|
||||
"""
|
||||
This view allows an authenticated user with the appropriate model permissions
|
||||
to read and write the current configuration for the specified `model`.
|
||||
|
||||
Like other APIViews, you can use this by using a url pattern similar to the following::
|
||||
|
||||
url(r'config/example_config$', ConfigurationModelCurrentAPIView.as_view(model=ExampleConfig))
|
||||
"""
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (ReadableOnlyByAuthors,)
|
||||
model = None
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
# Return the currently active configuration
|
||||
return self.model.current()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.serializer_class is None:
|
||||
class AutoConfigModelSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = self.model
|
||||
|
||||
self.serializer_class = AutoConfigModelSerializer
|
||||
|
||||
return self.serializer_class
|
||||
|
||||
def perform_create(self, serializer):
|
||||
# Set the requesting user as the one who is updating the configuration
|
||||
serializer.save(changed_by = self.request.user)
|
||||
Reference in New Issue
Block a user