Add CRUD views to bootstrap app, add support for dumping models into bootstrap fixtures

This commit is contained in:
Roberto Rosario
2012-09-24 17:34:30 -04:00
parent cb4c70c822
commit 7f7ba8924d
12 changed files with 312 additions and 31 deletions

View File

@@ -2,7 +2,11 @@ from __future__ import absolute_import
from navigation.api import bind_links
from .links import bootstrap_execute
from .links import (link_bootstrap_setup_create, link_bootstrap_setup_execute,
link_bootstrap_setup_list, link_bootstrap_setup_edit, link_bootstrap_setup_delete,
link_bootstrap_setup_view, link_bootstrap_setup_dump)
from .models import BootstrapSetup
bind_links([BootstrapSetup], [bootstrap_execute])
bind_links([BootstrapSetup], [link_bootstrap_setup_view, link_bootstrap_setup_edit, link_bootstrap_setup_delete, link_bootstrap_setup_execute])
bind_links([BootstrapSetup], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump], menu_name='secondary_menu')
bind_links(['bootstrap_setup_list', 'bootstrap_setup_create', 'bootstrap_setup_dump'], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump], menu_name='secondary_menu')

View File

@@ -16,3 +16,29 @@ class Cleanup(object):
self.name = name
self.function = function
self.__class__._registry[self.name] = self
class BootstrapModel(object):
"""
Class used to keep track of all the models to be dumped to create a
bootstrap setup from the current setup in use
"""
_registry = {}
@classmethod
def get_all(cls):
return cls._registry.values()
def get_fullname(self):
return '.'.join([self.app_name, self.model_name])
def __init__(self, model_name, app_name=None):
app_name_splitted = None
if '.' in model_name:
app_name_splitted, model_name = model_name.split('.')
self.app_name = app_name_splitted or app_name
if not self.app_name:
raise Exception('Pass either a dotted app plus model name or a model name and a separate app name')
self.model_name = model_name
self.__class__._registry[self.get_fullname()] = self

28
apps/bootstrap/forms.py Normal file
View File

@@ -0,0 +1,28 @@
from __future__ import absolute_import
import logging
from django import forms
from django.utils.translation import ugettext_lazy as _
from common.forms import DetailForm
from .models import BootstrapSetup
logger = logging.getLogger(__name__)
class BootstrapSetupForm(forms.ModelForm):
class Meta:
model = BootstrapSetup
class BootstrapSetupForm_view(DetailForm):
class Meta:
model = BootstrapSetup
class BootstrapSetupForm_dump(forms.ModelForm):
class Meta:
model = BootstrapSetup
exclude = ['fixture']

View File

@@ -1,8 +1,13 @@
from __future__ import absolute_import
from icons.literals import DATABASE_LIGHTNING, RADIOACTIVITY
from icons.literals import WIZARD, LIGHTNING, RADIOACTIVITY, MAGIC_WAND_2
from icons import Icon
icon_database_bootstrap = Icon(DATABASE_LIGHTNING)
icon_bootstrap_execute = Icon(DATABASE_LIGHTNING)
icon_bootstrap_setup = Icon(WIZARD)
icon_bootstrap_setup_view = Icon(WIZARD)
icon_bootstrap_setup_create = Icon(WIZARD)
icon_bootstrap_setup_edit = Icon(WIZARD)
icon_bootstrap_setup_delete = Icon(WIZARD)
icon_bootstrap_setup_execute = Icon(LIGHTNING)
icon_bootstrap_setup_dump = Icon(MAGIC_WAND_2)
icon_nuke_database = Icon(RADIOACTIVITY)

View File

@@ -4,9 +4,20 @@ from django.utils.translation import ugettext_lazy as _
from navigation import Link
from .permissions import PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE
from .icons import icon_database_bootstrap, icon_bootstrap_execute, icon_nuke_database
from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE,
PERMISSION_BOOTSTRAP_EDIT, PERMISSION_BOOTSTRAP_DELETE,
PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_BOOTSTRAP_DUMP,
PERMISSION_NUKE_DATABASE)
from .icons import (icon_bootstrap_setup, icon_bootstrap_setup_execute, icon_bootstrap_setup_create,
icon_bootstrap_setup_edit, icon_bootstrap_setup_delete, icon_bootstrap_setup_view,
icon_bootstrap_setup_dump, icon_nuke_database)
database_bootstrap = Link(text=_(u'bootstrap database'), view='bootstrap_type_list', icon=icon_database_bootstrap, permissions=[PERMISSION_BOOTSTRAP_EXECUTE])
bootstrap_execute = Link(text=_(u'execute'), view='bootstrap_execute', args='object.pk', icon=icon_bootstrap_execute, permissions=[PERMISSION_BOOTSTRAP_EXECUTE])
link_bootstrap_setup_tool = Link(text=_(u'bootstrap'), view='bootstrap_setup_list', icon=icon_bootstrap_setup, permissions=[PERMISSION_BOOTSTRAP_VIEW])
link_bootstrap_setup_list = Link(text=_(u'bootstrap setup list'), view='bootstrap_setup_list', icon=icon_bootstrap_setup, permissions=[PERMISSION_BOOTSTRAP_VIEW])
link_bootstrap_setup_create = Link(text=_(u'create new bootstrap setup'), view='bootstrap_setup_create', icon=icon_bootstrap_setup_create, permissions=[PERMISSION_BOOTSTRAP_CREATE])
link_bootstrap_setup_edit = Link(text=_(u'edit'), view='bootstrap_setup_edit', args='object.pk', icon=icon_bootstrap_setup_edit, permissions=[PERMISSION_BOOTSTRAP_EDIT])
link_bootstrap_setup_delete = Link(text=_(u'delete'), view='bootstrap_setup_delete', args='object.pk', icon=icon_bootstrap_setup_delete, permissions=[PERMISSION_BOOTSTRAP_DELETE])
link_bootstrap_setup_view = Link(text=_(u'details'), view='bootstrap_setup_view', args='object.pk', icon=icon_bootstrap_setup_view, permissions=[PERMISSION_BOOTSTRAP_VIEW])
link_bootstrap_setup_execute = Link(text=_(u'execute'), view='bootstrap_setup_execute', args='object.pk', icon=icon_bootstrap_setup_execute, permissions=[PERMISSION_BOOTSTRAP_EXECUTE])
link_bootstrap_setup_dump = Link(text=_(u'dump current setup'), view='bootstrap_setup_dump', icon=icon_bootstrap_setup_dump, permissions=[PERMISSION_BOOTSTRAP_DUMP])
link_erase_database = Link(text=_(u'erase database'), view='erase_database_view', icon=icon_nuke_database, permissions=[PERMISSION_NUKE_DATABASE])

View File

@@ -26,3 +26,6 @@ FIXTURE_TYPE_PK_NULLIFIER = {
FIXTURE_TYPE_YAML: lambda x: re.sub('pk: [0-9]{1,5}', 'pk: null', x),
FIXTURE_TYPE_XML: lambda x: re.sub('pk="[0-9]{1,5}"', 'pk=null', x),
}
COMMAND_LOADDATA = 'loaddata'
COMMAND_DUMPDATA = 'dumpdata'

View File

@@ -0,0 +1,35 @@
from __future__ import absolute_import
import logging
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.db import models
from django.core import management
from .classes import BootstrapModel
from .literals import COMMAND_DUMPDATA
logger = logging.getLogger(__name__)
class BootstrapSetupManager(models.Manager):
def explode(self, data):
"""
Gets a compressed and compacted bootstrap setup and creates a new
database BootstrapSetup instance
"""
pass
def dump(cls, format):
models = [instance.get_fullname() for instance in BootstrapModel.get_all()]
logger.debug('models: %s' % models)
result = StringIO()
options = dict(indent=4, format=format, use_natural_keys=True, interactive=False, verbosity=0, stdout=result)
management.call_command(COMMAND_DUMPDATA, *models, **options)
result.seek(0)
logger.debug('result: %s' % result)
return result.read()

View File

@@ -7,18 +7,22 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core import management
from .literals import FIXTURE_TYPES_CHOICES, FIXTURE_FILE_TYPE
from .literals import (FIXTURE_TYPES_CHOICES, FIXTURE_FILE_TYPE,
FIXTURE_TYPE_PK_NULLIFIER, COMMAND_LOADDATA)
from .managers import BootstrapSetupManager
class BootstrapSetup(models.Model):
"""
Model to store the fixture for a pre configured setup
Model to store the fixture for a pre configured setup.
"""
name = models.CharField(max_length=128, verbose_name=_(u'name'), unique=True)
description = models.TextField(verbose_name=_(u'description'), blank=True)
fixture = models.TextField(verbose_name=_(u'fixture'))
fixture = models.TextField(verbose_name=_(u'fixture'), help_text=_(u'These are the actual database structure creation instructions.'))
type = models.CharField(max_length=16, verbose_name=_(u'type'), choices=FIXTURE_TYPES_CHOICES)
objects = BootstrapSetupManager()
def __unicode__(self):
return self.name
@@ -35,9 +39,27 @@ class BootstrapSetup(models.Model):
with open(filepath, 'w') as file_handle:
file_handle.write(self.fixture)
management.call_command('loaddata', filepath, verbosity=0)
management.call_command(COMMAND_LOADDATA, filepath, verbosity=0)
os.unlink(filepath)
def compress(self):
"""
Return a compacted and compressed version of the BootstrapSetup
instance, meant for download.
"""
return ''
def sanitize(self):
"""
Remove pk values
"""
self.fixture = FIXTURE_TYPE_PK_NULLIFIER[self.type](self.fixture)
def save(self, *args, **kwargs):
self.sanitize()
return super(BootstrapSetup, self).save(*args, **kwargs)
class Meta:
verbose_name = _(u'bootstrap setup')
verbose_name_plural = _(u'bootstrap setups')
ordering = ['name']

View File

@@ -6,5 +6,10 @@ from permissions.models import PermissionNamespace, Permission
namespace = PermissionNamespace('bootstrap', _(u'Database bootstrap'))
PERMISSION_BOOTSTRAP_EXECUTE = Permission.objects.register(namespace, 'bootstrap_execute', _(u'Execute document bootstraps'))
PERMISSION_BOOTSTRAP_VIEW = Permission.objects.register(namespace, 'bootstrap_view', _(u'View bootstrap setups'))
PERMISSION_BOOTSTRAP_CREATE = Permission.objects.register(namespace, 'bootstrap_create', _(u'Create bootstrap setups'))
PERMISSION_BOOTSTRAP_EDIT = Permission.objects.register(namespace, 'bootstrap_edit', _(u'Edit bootstrap setups'))
PERMISSION_BOOTSTRAP_DELETE = Permission.objects.register(namespace, 'bootstrap_delete', _(u'Delete bootstrap setups'))
PERMISSION_BOOTSTRAP_EXECUTE = Permission.objects.register(namespace, 'bootstrap_execute', _(u'Execute bootstrap setups'))
PERMISSION_BOOTSTRAP_DUMP = Permission.objects.register(namespace, 'bootstrap_dump', _(u'Dump the current project\s setup into a bootstrap setup'))
PERMISSION_NUKE_DATABASE = Permission.objects.register(namespace, 'nuke_database', _(u'Erase the entire database and document storage'))

View File

@@ -2,11 +2,11 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
from .icons import icon_database_bootstrap
from .links import database_bootstrap, link_erase_database
from .icons import icon_bootstrap_setup
from .links import link_bootstrap_setup_tool, link_erase_database
label = _(u'Database bootstrap')
description = _(u'Provides pre configured setups for indexes, document types, tags.')
label = _(u'Bootstrap')
description = _(u'Provides pre configured setups for indexes, document types, tags, etc.')
dependencies = ['app_registry', 'icons', 'navigation', 'documents', 'indexing', 'metadata', 'tags']
icon = icon_database_bootstrap
setup_links = [database_bootstrap, link_erase_database]
icon = icon_bootstrap_setup
setup_links = [link_bootstrap_setup_tool, link_erase_database]

View File

@@ -1,7 +1,12 @@
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('bootstrap.views',
url(r'^type/list/$', 'bootstrap_type_list', (), 'bootstrap_type_list'),
url(r'^(?P<bootstrap_setup_pk>\d+)/execute/$', 'bootstrap_execute', (), 'bootstrap_execute'),
url(r'^setup/list/$', 'bootstrap_setup_list', (), 'bootstrap_setup_list'),
url(r'^setup/create/$', 'bootstrap_setup_create', (), 'bootstrap_setup_create'),
url(r'^setup/(?P<bootstrap_setup_pk>\d+)/edit/$', 'bootstrap_setup_edit', (), 'bootstrap_setup_edit'),
url(r'^setup/(?P<bootstrap_setup_pk>\d+)/delete/$', 'bootstrap_setup_delete', (), 'bootstrap_setup_delete'),
url(r'^setup/(?P<bootstrap_setup_pk>\d+)/$', 'bootstrap_setup_view', (), 'bootstrap_setup_view'),
url(r'^setup/(?P<bootstrap_setup_pk>\d+)/execute/$', 'bootstrap_setup_execute', (), 'bootstrap_setup_execute'),
url(r'^setup/dump/$', 'bootstrap_setup_dump', (), 'bootstrap_setup_dump'),
url(r'^nuke/$', 'erase_database_view', (), 'erase_database_view'),
)

View File

@@ -10,20 +10,24 @@ from django.core.urlresolvers import reverse
from permissions.models import Permission
from .models import BootstrapSetup
from .classes import Cleanup
from .permissions import PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE
from .icons import icon_bootstrap_execute, icon_nuke_database
from .classes import Cleanup, BootstrapModel
from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE,
PERMISSION_BOOTSTRAP_EDIT, PERMISSION_BOOTSTRAP_DELETE,
PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE, PERMISSION_BOOTSTRAP_DUMP)
from .icons import icon_bootstrap_setup_execute, icon_nuke_database, icon_bootstrap_setup_delete
from .forms import BootstrapSetupForm, BootstrapSetupForm_view, BootstrapSetupForm_dump
def bootstrap_type_list(request):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EXECUTE])
def bootstrap_setup_list(request):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_VIEW])
context = {
'object_list': BootstrapSetup.objects.all(),
'title': _(u'database bootstrap setups'),
'title': _(u'bootstrap setups'),
'hide_link': True,
'extra_columns': [
{'name': _(u'description'), 'attribute': 'description'},
{'name': _(u'type'), 'attribute': 'get_type_display'},
],
}
@@ -31,11 +35,119 @@ def bootstrap_type_list(request):
context_instance=RequestContext(request))
def bootstrap_execute(request, bootstrap_setup_pk):
def bootstrap_setup_create(request):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_CREATE])
if request.method == 'POST':
form = BootstrapSetupForm(request.POST)
if form.is_valid():
bootstrap = form.save()
messages.success(request, _(u'Bootstrap created successfully'))
return HttpResponseRedirect(reverse('bootstrap_setup_list'))
else:
messages.error(request, _(u'Error creating bootstrap setup.'))
else:
form = BootstrapSetupForm()
return render_to_response('generic_form.html', {
'title': _(u'create bootstrap'),
'form': form,
},
context_instance=RequestContext(request))
def bootstrap_setup_edit(request, bootstrap_setup_pk):
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EDIT])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_EDIT, request.user, bootstrap)
if request.method == 'POST':
form = BootstrapSetupForm(instance=bootstrap, data=request.POST)
if form.is_valid():
form.save()
messages.success(request, _(u'Bootstrap setup edited successfully'))
return HttpResponseRedirect(previous)
else:
messages.error(request, _(u'Error editing bootstrap setup.'))
else:
form = BootstrapSetupForm(instance=bootstrap)
return render_to_response('generic_form.html', {
'title': _(u'edit bootstrap setup: %s') % bootstrap,
'form': form,
'object': bootstrap,
'previous': previous,
'object_name': _(u'bootstrap setup'),
},
context_instance=RequestContext(request))
def bootstrap_setup_delete(request, bootstrap_setup_pk):
bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_DELETE])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_DELETE, request.user, bootstrap)
post_action_redirect = reverse('bootstrap_setup_list')
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
if request.method == 'POST':
try:
bootstrap.delete()
messages.success(request, _(u'Bootstrap setup: %s deleted successfully.') % bootstrap)
except Exception, e:
messages.error(request, _(u'Bootstrap setup: %(bootstrap)s delete error: %(error)s') % {
'bootstrap': bootstrap, 'error': e})
return HttpResponseRedirect(reverse('bootstrap_setup_list'))
context = {
'object_name': _(u'bootstrap setup'),
'delete_view': True,
'previous': previous,
'next': next,
'object': bootstrap,
'title': _(u'Are you sure you with to delete the bootstrap setup: %s?') % bootstrap,
'form_icon': icon_bootstrap_setup_delete,
}
return render_to_response('generic_confirm.html', context,
context_instance=RequestContext(request))
def bootstrap_setup_view(request, bootstrap_setup_pk):
bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_VIEW])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_VIEW, request.user, bootstrap)
form = BootstrapSetupForm_view(instance=bootstrap)
context = {
'form': form,
'object': bootstrap,
'object_name': _(u'bootstrap setup'),
}
return render_to_response('generic_detail.html', context,
context_instance=RequestContext(request))
def bootstrap_setup_execute(request, bootstrap_setup_pk):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EXECUTE])
bootstrap_setup = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk)
post_action_redirect = reverse('bootstrap_type_list')
post_action_redirect = reverse('bootstrap_setup_list')
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
@@ -54,7 +166,7 @@ def bootstrap_execute(request, bootstrap_setup_pk):
'delete_view': False,
'previous': previous,
'next': next,
'form_icon': icon_bootstrap_execute,
'form_icon': icon_bootstrap_setup_execute,
'object': bootstrap_setup,
}
@@ -64,6 +176,31 @@ def bootstrap_execute(request, bootstrap_setup_pk):
context_instance=RequestContext(request))
def bootstrap_setup_dump(request):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_DUMP])
if request.method == 'POST':
form = BootstrapSetupForm_dump(request.POST)
if form.is_valid():
bootstrap = form.save(commit=False)
try:
bootstrap.fixture = BootstrapSetup.objects.dump(format=bootstrap.type)
except Exception as exception:
messages.error(request, _(u'Error dumping bootstrap setup; %s') % exception)
else:
bootstrap.save()
messages.success(request, _(u'Bootstrap created successfully.'))
return HttpResponseRedirect(reverse('bootstrap_setup_list'))
else:
form = BootstrapSetupForm_dump()
return render_to_response('generic_form.html', {
'title': _(u'dump current setup into a bootstrap setup'),
'form': form,
},
context_instance=RequestContext(request))
def erase_database_view(request):
Permission.objects.check_permissions(request.user, [PERMISSION_NUKE_DATABASE])