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 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 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.name = name
self.function = function self.function = function
self.__class__._registry[self.name] = self 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 __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 from icons import Icon
icon_database_bootstrap = Icon(DATABASE_LIGHTNING) icon_bootstrap_setup = Icon(WIZARD)
icon_bootstrap_execute = Icon(DATABASE_LIGHTNING) 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) icon_nuke_database = Icon(RADIOACTIVITY)

View File

@@ -4,9 +4,20 @@ from django.utils.translation import ugettext_lazy as _
from navigation import Link from navigation import Link
from .permissions import PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE,
from .icons import icon_database_bootstrap, icon_bootstrap_execute, icon_nuke_database 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]) link_bootstrap_setup_tool = Link(text=_(u'bootstrap'), view='bootstrap_setup_list', icon=icon_bootstrap_setup, permissions=[PERMISSION_BOOTSTRAP_VIEW])
bootstrap_execute = Link(text=_(u'execute'), view='bootstrap_execute', args='object.pk', icon=icon_bootstrap_execute, permissions=[PERMISSION_BOOTSTRAP_EXECUTE]) 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]) 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_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), 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.utils.translation import ugettext_lazy as _
from django.core import management 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): 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) name = models.CharField(max_length=128, verbose_name=_(u'name'), unique=True)
description = models.TextField(verbose_name=_(u'description'), blank=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) type = models.CharField(max_length=16, verbose_name=_(u'type'), choices=FIXTURE_TYPES_CHOICES)
objects = BootstrapSetupManager()
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@@ -35,9 +39,27 @@ class BootstrapSetup(models.Model):
with open(filepath, 'w') as file_handle: with open(filepath, 'w') as file_handle:
file_handle.write(self.fixture) file_handle.write(self.fixture)
management.call_command('loaddata', filepath, verbosity=0) management.call_command(COMMAND_LOADDATA, filepath, verbosity=0)
os.unlink(filepath) 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: class Meta:
verbose_name = _(u'bootstrap setup') verbose_name = _(u'bootstrap setup')
verbose_name_plural = _(u'bootstrap setups') 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')) 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')) 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 django.utils.translation import ugettext_lazy as _
from .icons import icon_database_bootstrap from .icons import icon_bootstrap_setup
from .links import database_bootstrap, link_erase_database from .links import link_bootstrap_setup_tool, link_erase_database
label = _(u'Database bootstrap') label = _(u'Bootstrap')
description = _(u'Provides pre configured setups for indexes, document types, tags.') description = _(u'Provides pre configured setups for indexes, document types, tags, etc.')
dependencies = ['app_registry', 'icons', 'navigation', 'documents', 'indexing', 'metadata', 'tags'] dependencies = ['app_registry', 'icons', 'navigation', 'documents', 'indexing', 'metadata', 'tags']
icon = icon_database_bootstrap icon = icon_bootstrap_setup
setup_links = [database_bootstrap, link_erase_database] setup_links = [link_bootstrap_setup_tool, link_erase_database]

View File

@@ -1,7 +1,12 @@
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('bootstrap.views', urlpatterns = patterns('bootstrap.views',
url(r'^type/list/$', 'bootstrap_type_list', (), 'bootstrap_type_list'), url(r'^setup/list/$', 'bootstrap_setup_list', (), 'bootstrap_setup_list'),
url(r'^(?P<bootstrap_setup_pk>\d+)/execute/$', 'bootstrap_execute', (), 'bootstrap_execute'), 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'), 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 permissions.models import Permission
from .models import BootstrapSetup from .models import BootstrapSetup
from .classes import Cleanup from .classes import Cleanup, BootstrapModel
from .permissions import PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE,
from .icons import icon_bootstrap_execute, icon_nuke_database 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): def bootstrap_setup_list(request):
Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EXECUTE]) Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_VIEW])
context = { context = {
'object_list': BootstrapSetup.objects.all(), 'object_list': BootstrapSetup.objects.all(),
'title': _(u'database bootstrap setups'), 'title': _(u'bootstrap setups'),
'hide_link': True, 'hide_link': True,
'extra_columns': [ 'extra_columns': [
{'name': _(u'description'), 'attribute': 'description'}, {'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)) 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]) Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EXECUTE])
bootstrap_setup = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk) 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', '/'))) 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', '/'))) 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, 'delete_view': False,
'previous': previous, 'previous': previous,
'next': next, 'next': next,
'form_icon': icon_bootstrap_execute, 'form_icon': icon_bootstrap_setup_execute,
'object': bootstrap_setup, 'object': bootstrap_setup,
} }
@@ -64,6 +176,31 @@ def bootstrap_execute(request, bootstrap_setup_pk):
context_instance=RequestContext(request)) 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): def erase_database_view(request):
Permission.objects.check_permissions(request.user, [PERMISSION_NUKE_DATABASE]) Permission.objects.check_permissions(request.user, [PERMISSION_NUKE_DATABASE])